docker_toolkit 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -1
- data/Gemfile.lock +1 -1
- data/bin/terminator.rb +56 -0
- data/docker/Dockerfile +4 -1
- data/docker_toolkit.gemspec +1 -0
- data/lib/docker_toolkit/childprocess/abstract_io.rb +38 -0
- data/lib/docker_toolkit/childprocess/abstract_process.rb +194 -0
- data/lib/docker_toolkit/childprocess/errors.rb +28 -0
- data/lib/docker_toolkit/childprocess/jruby/io.rb +17 -0
- data/lib/docker_toolkit/childprocess/jruby/process.rb +161 -0
- data/lib/docker_toolkit/childprocess/jruby/pump.rb +55 -0
- data/lib/docker_toolkit/childprocess/jruby.rb +58 -0
- data/lib/docker_toolkit/childprocess/tools/generator.rb +148 -0
- data/lib/docker_toolkit/childprocess/unix/fork_exec_process.rb +72 -0
- data/lib/docker_toolkit/childprocess/unix/io.rb +22 -0
- data/lib/docker_toolkit/childprocess/unix/lib.rb +188 -0
- data/lib/docker_toolkit/childprocess/unix/platform/i386-linux.rb +14 -0
- data/lib/docker_toolkit/childprocess/unix/platform/i386-solaris.rb +13 -0
- data/lib/docker_toolkit/childprocess/unix/platform/x86_64-linux.rb +14 -0
- data/lib/docker_toolkit/childprocess/unix/platform/x86_64-macosx.rb +13 -0
- data/lib/docker_toolkit/childprocess/unix/posix_spawn_process.rb +135 -0
- data/lib/docker_toolkit/childprocess/unix/process.rb +91 -0
- data/lib/docker_toolkit/childprocess/unix.rb +11 -0
- data/lib/docker_toolkit/childprocess/version.rb +5 -0
- data/lib/docker_toolkit/childprocess/windows/handle.rb +93 -0
- data/lib/docker_toolkit/childprocess/windows/io.rb +25 -0
- data/lib/docker_toolkit/childprocess/windows/lib.rb +418 -0
- data/lib/docker_toolkit/childprocess/windows/process.rb +132 -0
- data/lib/docker_toolkit/childprocess/windows/process_builder.rb +177 -0
- data/lib/docker_toolkit/childprocess/windows/structs.rb +151 -0
- data/lib/docker_toolkit/childprocess/windows.rb +35 -0
- data/lib/docker_toolkit/childprocess.rb +208 -0
- data/lib/docker_toolkit/version.rb +1 -1
- data/lib/docker_toolkit/watcher.rb +188 -0
- data/lib/docker_toolkit.rb +5 -0
- metadata +33 -2
@@ -0,0 +1,132 @@
|
|
1
|
+
module DockerToolkit
|
2
|
+
module ChildProcess
|
3
|
+
module Windows
|
4
|
+
class Process < AbstractProcess
|
5
|
+
|
6
|
+
attr_reader :pid
|
7
|
+
|
8
|
+
def io
|
9
|
+
@io ||= Windows::IO.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def stop(timeout = 3)
|
13
|
+
assert_started
|
14
|
+
|
15
|
+
log "sending KILL"
|
16
|
+
@handle.send(WIN_SIGKILL)
|
17
|
+
|
18
|
+
poll_for_exit(timeout)
|
19
|
+
ensure
|
20
|
+
close_handle
|
21
|
+
close_job_if_necessary
|
22
|
+
end
|
23
|
+
|
24
|
+
def wait
|
25
|
+
if exited?
|
26
|
+
exit_code
|
27
|
+
else
|
28
|
+
@handle.wait
|
29
|
+
@exit_code = @handle.exit_code
|
30
|
+
|
31
|
+
close_handle
|
32
|
+
close_job_if_necessary
|
33
|
+
|
34
|
+
@exit_code
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def exited?
|
39
|
+
return true if @exit_code
|
40
|
+
assert_started
|
41
|
+
|
42
|
+
code = @handle.exit_code
|
43
|
+
exited = code != PROCESS_STILL_ACTIVE
|
44
|
+
|
45
|
+
log(:exited? => exited, :code => code)
|
46
|
+
|
47
|
+
if exited
|
48
|
+
@exit_code = code
|
49
|
+
close_handle
|
50
|
+
close_job_if_necessary
|
51
|
+
end
|
52
|
+
|
53
|
+
exited
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def launch_process
|
59
|
+
builder = ProcessBuilder.new(@args)
|
60
|
+
builder.leader = leader?
|
61
|
+
builder.detach = detach?
|
62
|
+
builder.duplex = duplex?
|
63
|
+
builder.environment = @environment unless @environment.empty?
|
64
|
+
builder.cwd = @cwd
|
65
|
+
|
66
|
+
if @io
|
67
|
+
builder.stdout = @io.stdout
|
68
|
+
builder.stderr = @io.stderr
|
69
|
+
end
|
70
|
+
|
71
|
+
@pid = builder.start
|
72
|
+
@handle = Handle.open @pid
|
73
|
+
|
74
|
+
if leader?
|
75
|
+
@job = Job.new
|
76
|
+
@job << @handle
|
77
|
+
end
|
78
|
+
|
79
|
+
if duplex?
|
80
|
+
raise Error, "no stdin stream" unless builder.stdin
|
81
|
+
io._stdin = builder.stdin
|
82
|
+
end
|
83
|
+
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def close_handle
|
88
|
+
@handle.close
|
89
|
+
end
|
90
|
+
|
91
|
+
def close_job_if_necessary
|
92
|
+
@job.close if leader?
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
class Job
|
97
|
+
def initialize
|
98
|
+
@pointer = Lib.create_job_object(nil, nil)
|
99
|
+
|
100
|
+
if @pointer.nil? || @pointer.null?
|
101
|
+
raise Error, "unable to create job object"
|
102
|
+
end
|
103
|
+
|
104
|
+
basic = JobObjectBasicLimitInformation.new
|
105
|
+
basic[:LimitFlags] = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_BREAKAWAY_OK
|
106
|
+
|
107
|
+
extended = JobObjectExtendedLimitInformation.new
|
108
|
+
extended[:BasicLimitInformation] = basic
|
109
|
+
|
110
|
+
ret = Lib.set_information_job_object(
|
111
|
+
@pointer,
|
112
|
+
JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
|
113
|
+
extended,
|
114
|
+
extended.size
|
115
|
+
)
|
116
|
+
|
117
|
+
Lib.check_error ret
|
118
|
+
end
|
119
|
+
|
120
|
+
def <<(handle)
|
121
|
+
Lib.check_error Lib.assign_process_to_job_object(@pointer, handle.pointer)
|
122
|
+
end
|
123
|
+
|
124
|
+
def close
|
125
|
+
Lib.close_handle @pointer
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end # Process
|
130
|
+
end # Windows
|
131
|
+
end # ChildProcess
|
132
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
module DockerToolkit
|
2
|
+
module ChildProcess
|
3
|
+
module Windows
|
4
|
+
class ProcessBuilder
|
5
|
+
attr_accessor :leader, :detach, :duplex, :environment, :stdout, :stderr, :cwd
|
6
|
+
attr_reader :stdin
|
7
|
+
|
8
|
+
def initialize(args)
|
9
|
+
@args = args
|
10
|
+
|
11
|
+
@detach = false
|
12
|
+
@duplex = false
|
13
|
+
@environment = nil
|
14
|
+
@cwd = nil
|
15
|
+
|
16
|
+
@stdout = nil
|
17
|
+
@stderr = nil
|
18
|
+
@stdin = nil
|
19
|
+
|
20
|
+
@flags = 0
|
21
|
+
@job_ptr = nil
|
22
|
+
@cmd_ptr = nil
|
23
|
+
@env_ptr = nil
|
24
|
+
@cwd_ptr = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
create_command_pointer
|
29
|
+
create_environment_pointer
|
30
|
+
create_cwd_pointer
|
31
|
+
|
32
|
+
setup_flags
|
33
|
+
setup_io
|
34
|
+
|
35
|
+
pid = create_process
|
36
|
+
close_handles
|
37
|
+
|
38
|
+
pid
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def create_command_pointer
|
44
|
+
string = @args.map { |arg| quote_if_necessary(arg.to_s) }.join ' '
|
45
|
+
@cmd_ptr = FFI::MemoryPointer.from_string string
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_environment_pointer
|
49
|
+
return unless @environment.kind_of?(Hash) && @environment.any?
|
50
|
+
|
51
|
+
strings = []
|
52
|
+
|
53
|
+
ENV.to_hash.merge(@environment).each do |key, val|
|
54
|
+
next if val.nil?
|
55
|
+
|
56
|
+
if key.to_s =~ /=|\0/ || val.to_s.include?("\0")
|
57
|
+
raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
|
58
|
+
end
|
59
|
+
|
60
|
+
strings << "#{key}=#{val}\0"
|
61
|
+
end
|
62
|
+
|
63
|
+
strings << "\0" # terminate the env block
|
64
|
+
env_str = strings.join
|
65
|
+
|
66
|
+
@env_ptr = FFI::MemoryPointer.new(:long, env_str.bytesize)
|
67
|
+
@env_ptr.put_bytes 0, env_str, 0, env_str.bytesize
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_cwd_pointer
|
71
|
+
@cwd_ptr = FFI::MemoryPointer.from_string(@cwd || Dir.pwd)
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_process
|
75
|
+
ok = Lib.create_process(
|
76
|
+
nil, # application name
|
77
|
+
@cmd_ptr, # command line
|
78
|
+
nil, # process attributes
|
79
|
+
nil, # thread attributes
|
80
|
+
true, # inherit handles
|
81
|
+
@flags, # creation flags
|
82
|
+
@env_ptr, # environment
|
83
|
+
@cwd_ptr, # current directory
|
84
|
+
startup_info, # startup info
|
85
|
+
process_info # process info
|
86
|
+
)
|
87
|
+
|
88
|
+
ok or raise LaunchError, Lib.last_error_message
|
89
|
+
|
90
|
+
process_info[:dwProcessId]
|
91
|
+
end
|
92
|
+
|
93
|
+
def startup_info
|
94
|
+
@startup_info ||= StartupInfo.new
|
95
|
+
end
|
96
|
+
|
97
|
+
def process_info
|
98
|
+
@process_info ||= ProcessInfo.new
|
99
|
+
end
|
100
|
+
|
101
|
+
def setup_flags
|
102
|
+
@flags |= DETACHED_PROCESS if @detach
|
103
|
+
@flags |= CREATE_BREAKAWAY_FROM_JOB if @leader
|
104
|
+
end
|
105
|
+
|
106
|
+
def setup_io
|
107
|
+
startup_info[:dwFlags] ||= 0
|
108
|
+
startup_info[:dwFlags] |= STARTF_USESTDHANDLES
|
109
|
+
|
110
|
+
if @stdout
|
111
|
+
startup_info[:hStdOutput] = std_stream_handle_for(@stdout)
|
112
|
+
end
|
113
|
+
|
114
|
+
if @stderr
|
115
|
+
startup_info[:hStdError] = std_stream_handle_for(@stderr)
|
116
|
+
end
|
117
|
+
|
118
|
+
if @duplex
|
119
|
+
read_pipe_ptr = FFI::MemoryPointer.new(:pointer)
|
120
|
+
write_pipe_ptr = FFI::MemoryPointer.new(:pointer)
|
121
|
+
sa = SecurityAttributes.new(:inherit => true)
|
122
|
+
|
123
|
+
ok = Lib.create_pipe(read_pipe_ptr, write_pipe_ptr, sa, 0)
|
124
|
+
Lib.check_error ok
|
125
|
+
|
126
|
+
@read_pipe = read_pipe_ptr.read_pointer
|
127
|
+
@write_pipe = write_pipe_ptr.read_pointer
|
128
|
+
|
129
|
+
Lib.set_handle_inheritance @read_pipe, true
|
130
|
+
Lib.set_handle_inheritance @write_pipe, false
|
131
|
+
|
132
|
+
startup_info[:hStdInput] = @read_pipe
|
133
|
+
else
|
134
|
+
startup_info[:hStdInput] = std_stream_handle_for(STDIN)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def std_stream_handle_for(io)
|
139
|
+
handle = Lib.handle_for(io)
|
140
|
+
|
141
|
+
begin
|
142
|
+
Lib.set_handle_inheritance handle, true
|
143
|
+
rescue ChildProcess::Error
|
144
|
+
# If the IO was set to close on exec previously, this call will fail.
|
145
|
+
# That's probably OK, since the user explicitly asked for it to be
|
146
|
+
# closed (at least I have yet to find other cases where this will
|
147
|
+
# happen...)
|
148
|
+
end
|
149
|
+
|
150
|
+
handle
|
151
|
+
end
|
152
|
+
|
153
|
+
def close_handles
|
154
|
+
Lib.close_handle process_info[:hProcess]
|
155
|
+
Lib.close_handle process_info[:hThread]
|
156
|
+
|
157
|
+
if @duplex
|
158
|
+
@stdin = Lib.io_for(Lib.duplicate_handle(@write_pipe), File::WRONLY)
|
159
|
+
Lib.close_handle @read_pipe
|
160
|
+
Lib.close_handle @write_pipe
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def quote_if_necessary(str)
|
165
|
+
quote = str.start_with?('"') ? "'" : '"'
|
166
|
+
|
167
|
+
case str
|
168
|
+
when /[\s\\'"]/
|
169
|
+
[quote, str, quote].join
|
170
|
+
else
|
171
|
+
str
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end # ProcessBuilder
|
175
|
+
end # Windows
|
176
|
+
end # ChildProcess
|
177
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module DockerToolkit
|
2
|
+
module ChildProcess
|
3
|
+
module Windows
|
4
|
+
# typedef struct _STARTUPINFO {
|
5
|
+
# DWORD cb;
|
6
|
+
# LPTSTR lpReserved;
|
7
|
+
# LPTSTR lpDesktop;
|
8
|
+
# LPTSTR lpTitle;
|
9
|
+
# DWORD dwX;
|
10
|
+
# DWORD dwY;
|
11
|
+
# DWORD dwXSize;
|
12
|
+
# DWORD dwYSize;
|
13
|
+
# DWORD dwXCountChars;
|
14
|
+
# DWORD dwYCountChars;
|
15
|
+
# DWORD dwFillAttribute;
|
16
|
+
# DWORD dwFlags;
|
17
|
+
# WORD wShowWindow;
|
18
|
+
# WORD cbReserved2;
|
19
|
+
# LPBYTE lpReserved2;
|
20
|
+
# HANDLE hStdInput;
|
21
|
+
# HANDLE hStdOutput;
|
22
|
+
# HANDLE hStdError;
|
23
|
+
# } STARTUPINFO, *LPSTARTUPINFO;
|
24
|
+
|
25
|
+
class StartupInfo < FFI::Struct
|
26
|
+
layout :cb, :ulong,
|
27
|
+
:lpReserved, :pointer,
|
28
|
+
:lpDesktop, :pointer,
|
29
|
+
:lpTitle, :pointer,
|
30
|
+
:dwX, :ulong,
|
31
|
+
:dwY, :ulong,
|
32
|
+
:dwXSize, :ulong,
|
33
|
+
:dwYSize, :ulong,
|
34
|
+
:dwXCountChars, :ulong,
|
35
|
+
:dwYCountChars, :ulong,
|
36
|
+
:dwFillAttribute, :ulong,
|
37
|
+
:dwFlags, :ulong,
|
38
|
+
:wShowWindow, :ushort,
|
39
|
+
:cbReserved2, :ushort,
|
40
|
+
:lpReserved2, :pointer,
|
41
|
+
:hStdInput, :pointer, # void ptr
|
42
|
+
:hStdOutput, :pointer, # void ptr
|
43
|
+
:hStdError, :pointer # void ptr
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# typedef struct _PROCESS_INFORMATION {
|
48
|
+
# HANDLE hProcess;
|
49
|
+
# HANDLE hThread;
|
50
|
+
# DWORD dwProcessId;
|
51
|
+
# DWORD dwThreadId;
|
52
|
+
# } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
|
53
|
+
#
|
54
|
+
|
55
|
+
class ProcessInfo < FFI::Struct
|
56
|
+
layout :hProcess, :pointer, # void ptr
|
57
|
+
:hThread, :pointer, # void ptr
|
58
|
+
:dwProcessId, :ulong,
|
59
|
+
:dwThreadId, :ulong
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# typedef struct _SECURITY_ATTRIBUTES {
|
64
|
+
# DWORD nLength;
|
65
|
+
# LPVOID lpSecurityDescriptor;
|
66
|
+
# BOOL bInheritHandle;
|
67
|
+
# } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
|
68
|
+
#
|
69
|
+
|
70
|
+
class SecurityAttributes < FFI::Struct
|
71
|
+
layout :nLength, :ulong,
|
72
|
+
:lpSecurityDescriptor, :pointer, # void ptr
|
73
|
+
:bInheritHandle, :int
|
74
|
+
|
75
|
+
def initialize(opts = {})
|
76
|
+
super()
|
77
|
+
|
78
|
+
self[:nLength] = self.class.size
|
79
|
+
self[:lpSecurityDescriptor] = nil
|
80
|
+
self[:bInheritHandle] = opts[:inherit] ? 1 : 0
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION {
|
86
|
+
# LARGE_INTEGER PerProcessUserTimeLimit;
|
87
|
+
# LARGE_INTEGER PerJobUserTimeLimit;
|
88
|
+
# DWORD LimitFlags;
|
89
|
+
# SIZE_T MinimumWorkingSetSize;
|
90
|
+
# SIZE_T MaximumWorkingSetSize;
|
91
|
+
# DWORD ActiveProcessLimit;
|
92
|
+
# ULONG_PTR Affinity;
|
93
|
+
# DWORD PriorityClass;
|
94
|
+
# DWORD SchedulingClass;
|
95
|
+
# } JOBOBJECT_BASIC_LIMIT_INFORMATION, *PJOBOBJECT_BASIC_LIMIT_INFORMATION;
|
96
|
+
#
|
97
|
+
class JobObjectBasicLimitInformation < FFI::Struct
|
98
|
+
layout :PerProcessUserTimeLimit, :int64,
|
99
|
+
:PerJobUserTimeLimit, :int64,
|
100
|
+
:LimitFlags, :ulong,
|
101
|
+
:MinimumWorkingSetSize, :size_t,
|
102
|
+
:MaximumWorkingSetSize, :size_t,
|
103
|
+
:ActiveProcessLimit, :ulong,
|
104
|
+
:Affinity, :pointer,
|
105
|
+
:PriorityClass, :ulong,
|
106
|
+
:SchedulingClass, :ulong
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# typedef struct _IO_COUNTERS {
|
111
|
+
# ULONGLONG ReadOperationCount;
|
112
|
+
# ULONGLONG WriteOperationCount;
|
113
|
+
# ULONGLONG OtherOperationCount;
|
114
|
+
# ULONGLONG ReadTransferCount;
|
115
|
+
# ULONGLONG WriteTransferCount;
|
116
|
+
# ULONGLONG OtherTransferCount;
|
117
|
+
# } IO_COUNTERS, *PIO_COUNTERS;
|
118
|
+
#
|
119
|
+
|
120
|
+
class IoCounters < FFI::Struct
|
121
|
+
layout :ReadOperationCount, :ulong_long,
|
122
|
+
:WriteOperationCount, :ulong_long,
|
123
|
+
:OtherOperationCount, :ulong_long,
|
124
|
+
:ReadTransferCount, :ulong_long,
|
125
|
+
:WriteTransferCount, :ulong_long,
|
126
|
+
:OtherTransferCount, :ulong_long
|
127
|
+
end
|
128
|
+
#
|
129
|
+
# typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
|
130
|
+
# JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
|
131
|
+
# IO_COUNTERS IoInfo;
|
132
|
+
# SIZE_T ProcessMemoryLimit;
|
133
|
+
# SIZE_T JobMemoryLimit;
|
134
|
+
# SIZE_T PeakProcessMemoryUsed;
|
135
|
+
# SIZE_T PeakJobMemoryUsed;
|
136
|
+
# } JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;
|
137
|
+
#
|
138
|
+
|
139
|
+
class JobObjectExtendedLimitInformation < FFI::Struct
|
140
|
+
layout :BasicLimitInformation, JobObjectBasicLimitInformation,
|
141
|
+
:IoInfo, IoCounters,
|
142
|
+
:ProcessMemoryLimit, :size_t,
|
143
|
+
:JobMemoryLimit, :size_t,
|
144
|
+
:PeakProcessMemoryUsed, :size_t,
|
145
|
+
:PeakJobMemoryUsed, :size_t
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
end # Windows
|
150
|
+
end # ChildProcess
|
151
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "ffi"
|
2
|
+
require "rbconfig"
|
3
|
+
|
4
|
+
module DockerToolkit
|
5
|
+
module ChildProcess
|
6
|
+
module Windows
|
7
|
+
module Lib
|
8
|
+
extend FFI::Library
|
9
|
+
|
10
|
+
def self.msvcrt_name
|
11
|
+
host_part = RbConfig::CONFIG['host_os'].split("_")[1]
|
12
|
+
manifest = File.join(RbConfig::CONFIG['bindir'], 'ruby.exe.manifest')
|
13
|
+
|
14
|
+
if host_part && host_part.to_i > 80 && File.exists?(manifest)
|
15
|
+
"msvcr#{host_part}"
|
16
|
+
else
|
17
|
+
"msvcrt"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
ffi_lib "kernel32", msvcrt_name
|
22
|
+
ffi_convention :stdcall
|
23
|
+
|
24
|
+
|
25
|
+
end # Library
|
26
|
+
end # Windows
|
27
|
+
end # ChildProcess
|
28
|
+
end DockerToolkit
|
29
|
+
|
30
|
+
require "docker_toolkit/childprocess/windows/lib"
|
31
|
+
require "docker_toolkit/childprocess/windows/structs"
|
32
|
+
require "docker_toolkit/childprocess/windows/handle"
|
33
|
+
require "docker_toolkit/childprocess/windows/io"
|
34
|
+
require "docker_toolkit/childprocess/windows/process_builder"
|
35
|
+
require "docker_toolkit/childprocess/windows/process"
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'docker_toolkit/childprocess/version'
|
2
|
+
require 'docker_toolkit/childprocess/errors'
|
3
|
+
require 'docker_toolkit/childprocess/abstract_process'
|
4
|
+
require 'docker_toolkit/childprocess/abstract_io'
|
5
|
+
require "fcntl"
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
module DockerToolkit
|
9
|
+
|
10
|
+
module ChildProcess
|
11
|
+
|
12
|
+
@posix_spawn = false
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_writer :logger
|
16
|
+
|
17
|
+
def new(*args)
|
18
|
+
case os
|
19
|
+
when :macosx, :linux, :solaris, :bsd, :cygwin, :aix
|
20
|
+
if posix_spawn?
|
21
|
+
Unix::PosixSpawnProcess.new(args)
|
22
|
+
elsif jruby?
|
23
|
+
JRuby::Process.new(args)
|
24
|
+
else
|
25
|
+
Unix::ForkExecProcess.new(args)
|
26
|
+
end
|
27
|
+
when :windows
|
28
|
+
Windows::Process.new(args)
|
29
|
+
else
|
30
|
+
raise Error, "unsupported platform #{platform_name.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :build, :new
|
34
|
+
|
35
|
+
def logger
|
36
|
+
return @logger if defined?(@logger) and @logger
|
37
|
+
|
38
|
+
@logger = Logger.new($stderr)
|
39
|
+
@logger.level = $DEBUG ? Logger::DEBUG : Logger::INFO
|
40
|
+
|
41
|
+
@logger
|
42
|
+
end
|
43
|
+
|
44
|
+
def platform
|
45
|
+
if RUBY_PLATFORM == "java"
|
46
|
+
:jruby
|
47
|
+
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby"
|
48
|
+
:ironruby
|
49
|
+
else
|
50
|
+
os
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def platform_name
|
55
|
+
@platform_name ||= "#{arch}-#{os}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def unix?
|
59
|
+
!windows?
|
60
|
+
end
|
61
|
+
|
62
|
+
def linux?
|
63
|
+
os == :linux
|
64
|
+
end
|
65
|
+
|
66
|
+
def jruby?
|
67
|
+
platform == :jruby
|
68
|
+
end
|
69
|
+
|
70
|
+
def windows?
|
71
|
+
os == :windows
|
72
|
+
end
|
73
|
+
|
74
|
+
def posix_spawn?
|
75
|
+
enabled = @posix_spawn || %w[1 true].include?(ENV['CHILDPROCESS_POSIX_SPAWN'])
|
76
|
+
return false unless enabled
|
77
|
+
|
78
|
+
require 'ffi'
|
79
|
+
begin
|
80
|
+
require "childprocess/unix/platform/#{ChildProcess.platform_name}"
|
81
|
+
rescue LoadError
|
82
|
+
raise ChildProcess::MissingPlatformError
|
83
|
+
end
|
84
|
+
|
85
|
+
require "childprocess/unix/lib"
|
86
|
+
require 'childprocess/unix/posix_spawn_process'
|
87
|
+
|
88
|
+
true
|
89
|
+
rescue ChildProcess::MissingPlatformError => ex
|
90
|
+
warn_once ex.message
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Set this to true to enable experimental use of posix_spawn.
|
96
|
+
#
|
97
|
+
|
98
|
+
def posix_spawn=(bool)
|
99
|
+
@posix_spawn = bool
|
100
|
+
end
|
101
|
+
|
102
|
+
def os
|
103
|
+
@os ||= (
|
104
|
+
require "rbconfig"
|
105
|
+
host_os = RbConfig::CONFIG['host_os'].downcase
|
106
|
+
|
107
|
+
case host_os
|
108
|
+
when /linux/
|
109
|
+
:linux
|
110
|
+
when /darwin|mac os/
|
111
|
+
:macosx
|
112
|
+
when /mswin|msys|mingw32/
|
113
|
+
:windows
|
114
|
+
when /cygwin/
|
115
|
+
:cygwin
|
116
|
+
when /solaris|sunos/
|
117
|
+
:solaris
|
118
|
+
when /bsd|dragonfly/
|
119
|
+
:bsd
|
120
|
+
when /aix/
|
121
|
+
:aix
|
122
|
+
else
|
123
|
+
raise Error, "unknown os: #{host_os.inspect}"
|
124
|
+
end
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def arch
|
129
|
+
@arch ||= (
|
130
|
+
host_cpu = RbConfig::CONFIG['host_cpu'].downcase
|
131
|
+
case host_cpu
|
132
|
+
when /i[3456]86/
|
133
|
+
if workaround_older_macosx_misreported_cpu?
|
134
|
+
# Workaround case: older 64-bit Darwin Rubies misreported as i686
|
135
|
+
"x86_64"
|
136
|
+
else
|
137
|
+
"i386"
|
138
|
+
end
|
139
|
+
when /amd64|x86_64/
|
140
|
+
"x86_64"
|
141
|
+
when /ppc|powerpc/
|
142
|
+
"powerpc"
|
143
|
+
else
|
144
|
+
host_cpu
|
145
|
+
end
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# By default, a child process will inherit open file descriptors from the
|
151
|
+
# parent process. This helper provides a cross-platform way of making sure
|
152
|
+
# that doesn't happen for the given file/io.
|
153
|
+
#
|
154
|
+
|
155
|
+
def close_on_exec(file)
|
156
|
+
if file.respond_to?(:close_on_exec=)
|
157
|
+
file.close_on_exec = true
|
158
|
+
elsif file.respond_to?(:fcntl) && defined?(Fcntl::FD_CLOEXEC)
|
159
|
+
file.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
|
160
|
+
|
161
|
+
if jruby? && posix_spawn?
|
162
|
+
# on JRuby, the fcntl call above apparently isn't enough when
|
163
|
+
# we're launching the process through posix_spawn.
|
164
|
+
fileno = JRuby.posix_fileno_for(file)
|
165
|
+
Unix::Lib.fcntl fileno, Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
|
166
|
+
end
|
167
|
+
elsif windows?
|
168
|
+
Windows::Lib.dont_inherit file
|
169
|
+
else
|
170
|
+
raise Error, "not sure how to set close-on-exec for #{file.inspect} on #{platform_name.inspect}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def warn_once(msg)
|
177
|
+
@warnings ||= {}
|
178
|
+
|
179
|
+
unless @warnings[msg]
|
180
|
+
@warnings[msg] = true
|
181
|
+
logger.warn msg
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Workaround: detect the situation that an older Darwin Ruby is actually
|
186
|
+
# 64-bit, but is misreporting cpu as i686, which would imply 32-bit.
|
187
|
+
#
|
188
|
+
# @return [Boolean] `true` if:
|
189
|
+
# (a) on Mac OS X
|
190
|
+
# (b) actually running in 64-bit mode
|
191
|
+
def workaround_older_macosx_misreported_cpu?
|
192
|
+
os == :macosx && is_64_bit?
|
193
|
+
end
|
194
|
+
|
195
|
+
# @return [Boolean] `true` if this Ruby represents `1` in 64 bits (8 bytes).
|
196
|
+
def is_64_bit?
|
197
|
+
1.size == 8
|
198
|
+
end
|
199
|
+
|
200
|
+
end # class << self
|
201
|
+
end # ChildProcess
|
202
|
+
end # DockerToolkit
|
203
|
+
|
204
|
+
require 'jruby' if DockerToolkit::ChildProcess.jruby?
|
205
|
+
|
206
|
+
require 'docker_toolkit/childprocess/unix' if DockerToolkit::ChildProcess.unix?
|
207
|
+
require 'docker_toolkit/childprocess/windows' if DockerToolkit::ChildProcess.windows?
|
208
|
+
require 'docker_toolkit/childprocess/jruby' if DockerToolkit::ChildProcess.jruby?
|