childprocess 1.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.document +6 -6
  3. data/.gitignore +28 -28
  4. data/.rspec +1 -1
  5. data/.travis.yml +37 -52
  6. data/CHANGELOG.md +83 -56
  7. data/Gemfile +9 -18
  8. data/LICENSE +20 -20
  9. data/README.md +230 -200
  10. data/Rakefile +61 -61
  11. data/appveyor.yml +36 -60
  12. data/childprocess.gemspec +26 -29
  13. data/lib/childprocess.rb +210 -210
  14. data/lib/childprocess/abstract_io.rb +36 -36
  15. data/lib/childprocess/abstract_process.rb +192 -192
  16. data/lib/childprocess/errors.rb +37 -37
  17. data/lib/childprocess/jruby.rb +56 -56
  18. data/lib/childprocess/jruby/io.rb +16 -16
  19. data/lib/childprocess/jruby/process.rb +184 -184
  20. data/lib/childprocess/jruby/pump.rb +53 -53
  21. data/lib/childprocess/tools/generator.rb +145 -145
  22. data/lib/childprocess/unix.rb +9 -9
  23. data/lib/childprocess/unix/fork_exec_process.rb +78 -70
  24. data/lib/childprocess/unix/io.rb +21 -21
  25. data/lib/childprocess/unix/lib.rb +186 -186
  26. data/lib/childprocess/unix/platform/arm64-macosx.rb +11 -0
  27. data/lib/childprocess/unix/platform/i386-linux.rb +12 -12
  28. data/lib/childprocess/unix/platform/i386-solaris.rb +11 -11
  29. data/lib/childprocess/unix/platform/x86_64-linux.rb +12 -12
  30. data/lib/childprocess/unix/platform/x86_64-macosx.rb +11 -11
  31. data/lib/childprocess/unix/posix_spawn_process.rb +134 -134
  32. data/lib/childprocess/unix/process.rb +90 -89
  33. data/lib/childprocess/version.rb +3 -3
  34. data/lib/childprocess/windows.rb +38 -33
  35. data/lib/childprocess/windows/handle.rb +91 -91
  36. data/lib/childprocess/windows/io.rb +25 -25
  37. data/lib/childprocess/windows/lib.rb +416 -416
  38. data/lib/childprocess/windows/process.rb +131 -130
  39. data/lib/childprocess/windows/process_builder.rb +178 -178
  40. data/lib/childprocess/windows/structs.rb +148 -148
  41. data/spec/abstract_io_spec.rb +12 -12
  42. data/spec/childprocess_spec.rb +447 -447
  43. data/spec/io_spec.rb +228 -228
  44. data/spec/jruby_spec.rb +24 -24
  45. data/spec/pid_behavior.rb +12 -12
  46. data/spec/platform_detection_spec.rb +86 -86
  47. data/spec/spec_helper.rb +270 -270
  48. data/spec/unix_spec.rb +57 -57
  49. data/spec/windows_spec.rb +23 -23
  50. metadata +12 -26
  51. data/ext/mkrf_conf.rb +0 -24
@@ -1,11 +1,11 @@
1
- module ChildProcess::Unix::Platform
2
- SIZEOF = {
3
- :posix_spawn_file_actions_t => 4,
4
- :posix_spawnattr_t => 4,
5
- :sigset_t => 16
6
- }
7
- POSIX_SPAWN_RESETIDS = 1
8
- POSIX_SPAWN_SETPGROUP = 2
9
- POSIX_SPAWN_SETSIGDEF = 4
10
- POSIX_SPAWN_SETSIGMASK = 8
11
- end
1
+ module ChildProcess::Unix::Platform
2
+ SIZEOF = {
3
+ :posix_spawn_file_actions_t => 4,
4
+ :posix_spawnattr_t => 4,
5
+ :sigset_t => 16
6
+ }
7
+ POSIX_SPAWN_RESETIDS = 1
8
+ POSIX_SPAWN_SETPGROUP = 2
9
+ POSIX_SPAWN_SETSIGDEF = 4
10
+ POSIX_SPAWN_SETSIGMASK = 8
11
+ end
@@ -1,12 +1,12 @@
1
- module ChildProcess::Unix::Platform
2
- SIZEOF = {
3
- :posix_spawn_file_actions_t => 80,
4
- :posix_spawnattr_t => 336,
5
- :sigset_t => 128
6
- }
7
- POSIX_SPAWN_RESETIDS = 1
8
- POSIX_SPAWN_SETPGROUP = 2
9
- POSIX_SPAWN_SETSIGDEF = 4
10
- POSIX_SPAWN_SETSIGMASK = 8
11
- POSIX_SPAWN_USEVFORK = 64
12
- end
1
+ module ChildProcess::Unix::Platform
2
+ SIZEOF = {
3
+ :posix_spawn_file_actions_t => 80,
4
+ :posix_spawnattr_t => 336,
5
+ :sigset_t => 128
6
+ }
7
+ POSIX_SPAWN_RESETIDS = 1
8
+ POSIX_SPAWN_SETPGROUP = 2
9
+ POSIX_SPAWN_SETSIGDEF = 4
10
+ POSIX_SPAWN_SETSIGMASK = 8
11
+ POSIX_SPAWN_USEVFORK = 64
12
+ end
@@ -1,11 +1,11 @@
1
- module ChildProcess::Unix::Platform
2
- SIZEOF = {
3
- :posix_spawn_file_actions_t => 8,
4
- :posix_spawnattr_t => 8,
5
- :sigset_t => 4
6
- }
7
- POSIX_SPAWN_RESETIDS = 1
8
- POSIX_SPAWN_SETPGROUP = 2
9
- POSIX_SPAWN_SETSIGDEF = 4
10
- POSIX_SPAWN_SETSIGMASK = 8
11
- end
1
+ module ChildProcess::Unix::Platform
2
+ SIZEOF = {
3
+ :posix_spawn_file_actions_t => 8,
4
+ :posix_spawnattr_t => 8,
5
+ :sigset_t => 4
6
+ }
7
+ POSIX_SPAWN_RESETIDS = 1
8
+ POSIX_SPAWN_SETPGROUP = 2
9
+ POSIX_SPAWN_SETSIGDEF = 4
10
+ POSIX_SPAWN_SETSIGMASK = 8
11
+ end
@@ -1,134 +1,134 @@
1
- require 'ffi'
2
- require 'thread'
3
-
4
- module ChildProcess
5
- module Unix
6
- class PosixSpawnProcess < Process
7
- private
8
-
9
- @@cwd_lock = Mutex.new
10
-
11
- def launch_process
12
- pid_ptr = FFI::MemoryPointer.new(:pid_t)
13
- actions = Lib::FileActions.new
14
- attrs = Lib::Attrs.new
15
-
16
- if io.stdout
17
- actions.add_dup fileno_for(io.stdout), fileno_for(STDOUT)
18
- else
19
- actions.add_open fileno_for(STDOUT), "/dev/null", File::WRONLY, 0644
20
- end
21
-
22
- if io.stderr
23
- actions.add_dup fileno_for(io.stderr), fileno_for(STDERR)
24
- else
25
- actions.add_open fileno_for(STDERR), "/dev/null", File::WRONLY, 0644
26
- end
27
-
28
- if duplex?
29
- reader, writer = ::IO.pipe
30
- actions.add_dup fileno_for(reader), fileno_for(STDIN)
31
- actions.add_close fileno_for(writer)
32
- end
33
-
34
- attrs.pgroup = 0 if leader?
35
- attrs.flags |= Platform::POSIX_SPAWN_USEVFORK if defined? Platform::POSIX_SPAWN_USEVFORK
36
-
37
- # wrap in helper classes in order to avoid GC'ed pointers
38
- argv = Argv.new(@args)
39
- envp = Envp.new(ENV.to_hash.merge(@environment))
40
-
41
- ret = 0
42
- @@cwd_lock.synchronize do
43
- Dir.chdir(@cwd || Dir.pwd) do
44
- if ChildProcess.jruby?
45
- # on JRuby, the current working directory is for some reason not inherited.
46
- # We'll work around it by making a chdir call through FFI.
47
- # TODO: report this to JRuby
48
- Lib.chdir Dir.pwd
49
- end
50
-
51
- ret = Lib.posix_spawnp(
52
- pid_ptr,
53
- @args.first, # TODO: not sure this matches exec() behaviour
54
- actions,
55
- attrs,
56
- argv,
57
- envp
58
- )
59
- end
60
- end
61
-
62
- if duplex?
63
- io._stdin = writer
64
- reader.close
65
- end
66
-
67
- actions.free
68
- attrs.free
69
-
70
- if ret != 0
71
- raise LaunchError, "#{Lib.strerror(ret)} (#{ret})"
72
- end
73
-
74
- @pid = pid_ptr.read_int
75
- ::Process.detach(@pid) if detach?
76
- end
77
-
78
- if ChildProcess.jruby?
79
- def fileno_for(obj)
80
- ChildProcess::JRuby.posix_fileno_for(obj)
81
- end
82
- else
83
- def fileno_for(obj)
84
- obj.fileno
85
- end
86
- end
87
-
88
- class Argv
89
- def initialize(args)
90
- @ptrs = args.map do |e|
91
- if e.include?("\0")
92
- raise ArgumentError, "argument cannot contain null bytes: #{e.inspect}"
93
- end
94
-
95
- FFI::MemoryPointer.from_string(e.to_s)
96
- end
97
-
98
- @ptrs << FFI::Pointer.new(0)
99
- end
100
-
101
- def to_ptr
102
- argv = FFI::MemoryPointer.new(:pointer, @ptrs.size)
103
- argv.put_array_of_pointer(0, @ptrs)
104
-
105
- argv
106
- end
107
- end # Argv
108
-
109
- class Envp
110
- def initialize(env)
111
- @ptrs = env.map do |key, val|
112
- next if val.nil?
113
-
114
- if key =~ /=|\0/ || val.to_s.include?("\0")
115
- raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.to_s.inspect}"
116
- end
117
-
118
- FFI::MemoryPointer.from_string("#{key}=#{val.to_s}")
119
- end.compact
120
-
121
- @ptrs << FFI::Pointer.new(0)
122
- end
123
-
124
- def to_ptr
125
- env = FFI::MemoryPointer.new(:pointer, @ptrs.size)
126
- env.put_array_of_pointer(0, @ptrs)
127
-
128
- env
129
- end
130
- end # Envp
131
-
132
- end
133
- end
134
- end
1
+ require 'ffi'
2
+ require 'thread'
3
+
4
+ module ChildProcess
5
+ module Unix
6
+ class PosixSpawnProcess < Process
7
+ private
8
+
9
+ @@cwd_lock = Mutex.new
10
+
11
+ def launch_process
12
+ pid_ptr = FFI::MemoryPointer.new(:pid_t)
13
+ actions = Lib::FileActions.new
14
+ attrs = Lib::Attrs.new
15
+
16
+ if io.stdout
17
+ actions.add_dup fileno_for(io.stdout), fileno_for(STDOUT)
18
+ else
19
+ actions.add_open fileno_for(STDOUT), "/dev/null", File::WRONLY, 0644
20
+ end
21
+
22
+ if io.stderr
23
+ actions.add_dup fileno_for(io.stderr), fileno_for(STDERR)
24
+ else
25
+ actions.add_open fileno_for(STDERR), "/dev/null", File::WRONLY, 0644
26
+ end
27
+
28
+ if duplex?
29
+ reader, writer = ::IO.pipe
30
+ actions.add_dup fileno_for(reader), fileno_for(STDIN)
31
+ actions.add_close fileno_for(writer)
32
+ end
33
+
34
+ attrs.pgroup = 0 if leader?
35
+ attrs.flags |= Platform::POSIX_SPAWN_USEVFORK if defined? Platform::POSIX_SPAWN_USEVFORK
36
+
37
+ # wrap in helper classes in order to avoid GC'ed pointers
38
+ argv = Argv.new(@args)
39
+ envp = Envp.new(ENV.to_hash.merge(@environment))
40
+
41
+ ret = 0
42
+ @@cwd_lock.synchronize do
43
+ Dir.chdir(@cwd || Dir.pwd) do
44
+ if ChildProcess.jruby?
45
+ # on JRuby, the current working directory is for some reason not inherited.
46
+ # We'll work around it by making a chdir call through FFI.
47
+ # TODO: report this to JRuby
48
+ Lib.chdir Dir.pwd
49
+ end
50
+
51
+ ret = Lib.posix_spawnp(
52
+ pid_ptr,
53
+ @args.first, # TODO: not sure this matches exec() behaviour
54
+ actions,
55
+ attrs,
56
+ argv,
57
+ envp
58
+ )
59
+ end
60
+ end
61
+
62
+ if duplex?
63
+ io._stdin = writer
64
+ reader.close
65
+ end
66
+
67
+ actions.free
68
+ attrs.free
69
+
70
+ if ret != 0
71
+ raise LaunchError, "#{Lib.strerror(ret)} (#{ret})"
72
+ end
73
+
74
+ @pid = pid_ptr.read_int
75
+ ::Process.detach(@pid) if detach?
76
+ end
77
+
78
+ if ChildProcess.jruby?
79
+ def fileno_for(obj)
80
+ ChildProcess::JRuby.posix_fileno_for(obj)
81
+ end
82
+ else
83
+ def fileno_for(obj)
84
+ obj.fileno
85
+ end
86
+ end
87
+
88
+ class Argv
89
+ def initialize(args)
90
+ @ptrs = args.map do |e|
91
+ if e.include?("\0")
92
+ raise ArgumentError, "argument cannot contain null bytes: #{e.inspect}"
93
+ end
94
+
95
+ FFI::MemoryPointer.from_string(e.to_s)
96
+ end
97
+
98
+ @ptrs << FFI::Pointer.new(0)
99
+ end
100
+
101
+ def to_ptr
102
+ argv = FFI::MemoryPointer.new(:pointer, @ptrs.size)
103
+ argv.put_array_of_pointer(0, @ptrs)
104
+
105
+ argv
106
+ end
107
+ end # Argv
108
+
109
+ class Envp
110
+ def initialize(env)
111
+ @ptrs = env.map do |key, val|
112
+ next if val.nil?
113
+
114
+ if key =~ /=|\0/ || val.to_s.include?("\0")
115
+ raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.to_s.inspect}"
116
+ end
117
+
118
+ FFI::MemoryPointer.from_string("#{key}=#{val.to_s}")
119
+ end.compact
120
+
121
+ @ptrs << FFI::Pointer.new(0)
122
+ end
123
+
124
+ def to_ptr
125
+ env = FFI::MemoryPointer.new(:pointer, @ptrs.size)
126
+ env.put_array_of_pointer(0, @ptrs)
127
+
128
+ env
129
+ end
130
+ end # Envp
131
+
132
+ end
133
+ end
134
+ end
@@ -1,89 +1,90 @@
1
- module ChildProcess
2
- module Unix
3
- class Process < AbstractProcess
4
- attr_reader :pid
5
-
6
- def io
7
- @io ||= Unix::IO.new
8
- end
9
-
10
- def stop(timeout = 3)
11
- assert_started
12
- send_term
13
-
14
- begin
15
- return poll_for_exit(timeout)
16
- rescue TimeoutError
17
- # try next
18
- end
19
-
20
- send_kill
21
- wait
22
- rescue Errno::ECHILD, Errno::ESRCH
23
- # handle race condition where process dies between timeout
24
- # and send_kill
25
- true
26
- end
27
-
28
- def exited?
29
- return true if @exit_code
30
-
31
- assert_started
32
- pid, status = ::Process.waitpid2(_pid, ::Process::WNOHANG | ::Process::WUNTRACED)
33
- pid = nil if pid == 0 # may happen on jruby
34
-
35
- log(:pid => pid, :status => status)
36
-
37
- if pid
38
- set_exit_code(status)
39
- end
40
-
41
- !!pid
42
- rescue Errno::ECHILD
43
- # may be thrown for detached processes
44
- true
45
- end
46
-
47
- def wait
48
- assert_started
49
-
50
- if exited?
51
- exit_code
52
- else
53
- _, status = ::Process.waitpid2 _pid
54
- set_exit_code(status)
55
- end
56
- end
57
-
58
- private
59
-
60
- def send_term
61
- send_signal 'TERM'
62
- end
63
-
64
- def send_kill
65
- send_signal 'KILL'
66
- end
67
-
68
- def send_signal(sig)
69
- assert_started
70
-
71
- log "sending #{sig}"
72
- ::Process.kill sig, _pid
73
- end
74
-
75
- def set_exit_code(status)
76
- @exit_code = status.exitstatus || status.termsig
77
- end
78
-
79
- def _pid
80
- if leader?
81
- -@pid # negative pid == process group
82
- else
83
- @pid
84
- end
85
- end
86
-
87
- end # Process
88
- end # Unix
89
- end # ChildProcess
1
+ module ChildProcess
2
+ module Unix
3
+ class Process < AbstractProcess
4
+ attr_reader :pid
5
+
6
+ def io
7
+ @io ||= Unix::IO.new
8
+ end
9
+
10
+ def stop(timeout = 3)
11
+ assert_started
12
+ send_term
13
+
14
+ begin
15
+ return poll_for_exit(timeout)
16
+ rescue TimeoutError
17
+ # try next
18
+ end
19
+
20
+ send_kill
21
+ wait
22
+ rescue Errno::ECHILD, Errno::ESRCH
23
+ # handle race condition where process dies between timeout
24
+ # and send_kill
25
+ true
26
+ end
27
+
28
+ def exited?
29
+ return true if @exit_code
30
+
31
+ assert_started
32
+ pid, status = ::Process.waitpid2(@pid, ::Process::WNOHANG | ::Process::WUNTRACED)
33
+ pid = nil if pid == 0 # may happen on jruby
34
+
35
+ log(:pid => pid, :status => status)
36
+
37
+ if pid
38
+ set_exit_code(status)
39
+ end
40
+
41
+ !!pid
42
+ rescue Errno::ECHILD
43
+ # may be thrown for detached processes
44
+ true
45
+ end
46
+
47
+ def wait
48
+ assert_started
49
+
50
+ if exited?
51
+ exit_code
52
+ else
53
+ _, status = ::Process.waitpid2(@pid)
54
+
55
+ set_exit_code(status)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def send_term
62
+ send_signal 'TERM'
63
+ end
64
+
65
+ def send_kill
66
+ send_signal 'KILL'
67
+ end
68
+
69
+ def send_signal(sig)
70
+ assert_started
71
+
72
+ log "sending #{sig}"
73
+ ::Process.kill sig, _pid
74
+ end
75
+
76
+ def set_exit_code(status)
77
+ @exit_code = status.exitstatus || status.termsig
78
+ end
79
+
80
+ def _pid
81
+ if leader?
82
+ -@pid # negative pid == process group
83
+ else
84
+ @pid
85
+ end
86
+ end
87
+
88
+ end # Process
89
+ end # Unix
90
+ end # ChildProcess