childprocess 0.8.0 → 3.0.0

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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.document +6 -6
  3. data/.gitignore +28 -28
  4. data/.rspec +1 -1
  5. data/.travis.yml +40 -36
  6. data/CHANGELOG.md +73 -44
  7. data/Gemfile +21 -15
  8. data/LICENSE +20 -20
  9. data/README.md +218 -192
  10. data/Rakefile +61 -61
  11. data/appveyor.yml +42 -43
  12. data/childprocess.gemspec +26 -30
  13. data/lib/childprocess.rb +210 -205
  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 -26
  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 -159
  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/i386-linux.rb +12 -12
  27. data/lib/childprocess/unix/platform/i386-solaris.rb +11 -11
  28. data/lib/childprocess/unix/platform/x86_64-linux.rb +12 -12
  29. data/lib/childprocess/unix/platform/x86_64-macosx.rb +11 -11
  30. data/lib/childprocess/unix/posix_spawn_process.rb +134 -134
  31. data/lib/childprocess/unix/process.rb +90 -89
  32. data/lib/childprocess/version.rb +3 -3
  33. data/lib/childprocess/windows.rb +38 -33
  34. data/lib/childprocess/windows/handle.rb +91 -91
  35. data/lib/childprocess/windows/io.rb +25 -25
  36. data/lib/childprocess/windows/lib.rb +416 -416
  37. data/lib/childprocess/windows/process.rb +130 -130
  38. data/lib/childprocess/windows/process_builder.rb +178 -175
  39. data/lib/childprocess/windows/structs.rb +148 -148
  40. data/spec/abstract_io_spec.rb +12 -12
  41. data/spec/childprocess_spec.rb +447 -391
  42. data/spec/get_env.ps1 +13 -0
  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 -261
  48. data/spec/unix_spec.rb +57 -57
  49. data/spec/windows_spec.rb +23 -23
  50. metadata +8 -39
@@ -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.include?("\0")
115
- raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
116
- end
117
-
118
- FFI::MemoryPointer.from_string("#{key}=#{val}")
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