docker_toolkit 0.1.2 → 0.1.3

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +10 -1
  3. data/Gemfile.lock +1 -1
  4. data/bin/terminator.rb +56 -0
  5. data/docker/Dockerfile +4 -1
  6. data/docker_toolkit.gemspec +1 -0
  7. data/lib/docker_toolkit/childprocess/abstract_io.rb +38 -0
  8. data/lib/docker_toolkit/childprocess/abstract_process.rb +194 -0
  9. data/lib/docker_toolkit/childprocess/errors.rb +28 -0
  10. data/lib/docker_toolkit/childprocess/jruby/io.rb +17 -0
  11. data/lib/docker_toolkit/childprocess/jruby/process.rb +161 -0
  12. data/lib/docker_toolkit/childprocess/jruby/pump.rb +55 -0
  13. data/lib/docker_toolkit/childprocess/jruby.rb +58 -0
  14. data/lib/docker_toolkit/childprocess/tools/generator.rb +148 -0
  15. data/lib/docker_toolkit/childprocess/unix/fork_exec_process.rb +72 -0
  16. data/lib/docker_toolkit/childprocess/unix/io.rb +22 -0
  17. data/lib/docker_toolkit/childprocess/unix/lib.rb +188 -0
  18. data/lib/docker_toolkit/childprocess/unix/platform/i386-linux.rb +14 -0
  19. data/lib/docker_toolkit/childprocess/unix/platform/i386-solaris.rb +13 -0
  20. data/lib/docker_toolkit/childprocess/unix/platform/x86_64-linux.rb +14 -0
  21. data/lib/docker_toolkit/childprocess/unix/platform/x86_64-macosx.rb +13 -0
  22. data/lib/docker_toolkit/childprocess/unix/posix_spawn_process.rb +135 -0
  23. data/lib/docker_toolkit/childprocess/unix/process.rb +91 -0
  24. data/lib/docker_toolkit/childprocess/unix.rb +11 -0
  25. data/lib/docker_toolkit/childprocess/version.rb +5 -0
  26. data/lib/docker_toolkit/childprocess/windows/handle.rb +93 -0
  27. data/lib/docker_toolkit/childprocess/windows/io.rb +25 -0
  28. data/lib/docker_toolkit/childprocess/windows/lib.rb +418 -0
  29. data/lib/docker_toolkit/childprocess/windows/process.rb +132 -0
  30. data/lib/docker_toolkit/childprocess/windows/process_builder.rb +177 -0
  31. data/lib/docker_toolkit/childprocess/windows/structs.rb +151 -0
  32. data/lib/docker_toolkit/childprocess/windows.rb +35 -0
  33. data/lib/docker_toolkit/childprocess.rb +208 -0
  34. data/lib/docker_toolkit/version.rb +1 -1
  35. data/lib/docker_toolkit/watcher.rb +188 -0
  36. data/lib/docker_toolkit.rb +5 -0
  37. metadata +33 -2
@@ -0,0 +1,148 @@
1
+ require 'fileutils'
2
+
3
+ module DockerToolkit
4
+ module ChildProcess
5
+ module Tools
6
+ class Generator
7
+ EXE_NAME = "childprocess-sizeof-generator"
8
+ TMP_PROGRAM = "childprocess-sizeof-generator.c"
9
+ DEFAULT_INCLUDES = %w[stdio.h stddef.h]
10
+
11
+ def self.generate
12
+ new.generate
13
+ end
14
+
15
+ def initialize
16
+ @cc = ENV['CC'] || 'gcc'
17
+ @out = File.expand_path("../../unix/platform/#{ChildProcess.platform_name}.rb", __FILE__)
18
+ @sizeof = {}
19
+ @constants = {}
20
+ end
21
+
22
+ def generate
23
+ fetch_size 'posix_spawn_file_actions_t', :include => "spawn.h"
24
+ fetch_size 'posix_spawnattr_t', :include => "spawn.h"
25
+ fetch_size 'sigset_t', :include => "signal.h"
26
+
27
+ fetch_constant 'POSIX_SPAWN_RESETIDS', :include => 'spawn.h'
28
+ fetch_constant 'POSIX_SPAWN_SETPGROUP', :include => 'spawn.h'
29
+ fetch_constant 'POSIX_SPAWN_SETSIGDEF', :include => 'spawn.h'
30
+ fetch_constant 'POSIX_SPAWN_SETSIGMASK', :include => 'spawn.h'
31
+
32
+ if ChildProcess.linux?
33
+ fetch_constant 'POSIX_SPAWN_USEVFORK', :include => 'spawn.h', :define => {'_GNU_SOURCE' => nil}
34
+ end
35
+
36
+ write
37
+ end
38
+
39
+ def write
40
+ FileUtils.mkdir_p(File.dirname(@out))
41
+ File.open(@out, 'w') do |io|
42
+ io.puts result
43
+ end
44
+
45
+ puts "wrote #{@out}"
46
+ end
47
+
48
+ def fetch_size(type_name, opts = {})
49
+ print "sizeof(#{type_name}): "
50
+ src = <<-EOF
51
+ int main() {
52
+ printf("%d", (unsigned int)sizeof(#{type_name}));
53
+ return 0;
54
+ }
55
+ EOF
56
+
57
+ output = execute(src, opts)
58
+
59
+ if output.to_i < 1
60
+ raise "sizeof(#{type_name}) == #{output.to_i} (output=#{output})"
61
+ end
62
+
63
+ size = output.to_i
64
+ @sizeof[type_name] = size
65
+
66
+ puts size
67
+ end
68
+
69
+ def fetch_constant(name, opts)
70
+ print "#{name}: "
71
+ src = <<-EOF
72
+ int main() {
73
+ printf("%d", (unsigned int)#{name});
74
+ return 0;
75
+ }
76
+ EOF
77
+
78
+ output = execute(src, opts)
79
+ value = Integer(output)
80
+ @constants[name] = value
81
+
82
+ puts value
83
+ end
84
+
85
+
86
+ def execute(src, opts)
87
+ program = Array(opts[:define]).map do |key, value|
88
+ <<-SRC
89
+ #ifndef #{key}
90
+ #define #{key} #{value}
91
+ #endif
92
+ SRC
93
+ end.join("\n")
94
+ program << "\n"
95
+
96
+ includes = Array(opts[:include]) + DEFAULT_INCLUDES
97
+ program << includes.map { |include| "#include <#{include}>" }.join("\n")
98
+ program << "\n#{src}"
99
+
100
+ File.open(TMP_PROGRAM, 'w') do |file|
101
+ file << program
102
+ end
103
+
104
+ cmd = "#{@cc} #{TMP_PROGRAM} -o #{EXE_NAME}"
105
+ system cmd
106
+ unless $?.success?
107
+ raise "failed to compile program: #{cmd.inspect}\n#{program}"
108
+ end
109
+
110
+ output = `./#{EXE_NAME} 2>&1`
111
+
112
+ unless $?.success?
113
+ raise "failed to run program: #{cmd.inspect}\n#{output}"
114
+ end
115
+
116
+ output.chomp
117
+ ensure
118
+ File.delete TMP_PROGRAM if File.exist?(TMP_PROGRAM)
119
+ File.delete EXE_NAME if File.exist?(EXE_NAME)
120
+ end
121
+
122
+ def result
123
+ if @sizeof.empty? && @constants.empty?
124
+ raise "no data collected, nothing to do"
125
+ end
126
+
127
+ out = ['module ChildProcess::Unix::Platform']
128
+ out << ' SIZEOF = {'
129
+
130
+ max = @sizeof.keys.map { |e| e.length }.max
131
+ @sizeof.each_with_index do |(type, size), idx|
132
+ out << " :#{type.ljust max} => #{size}#{',' unless idx == @sizeof.size - 1}"
133
+ end
134
+ out << ' }'
135
+
136
+ max = @constants.keys.map { |e| e.length }.max
137
+ @constants.each do |name, val|
138
+ out << " #{name.ljust max} = #{val}"
139
+ end
140
+ out << 'end'
141
+
142
+ out.join "\n"
143
+ end
144
+
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,72 @@
1
+ module DockerToolkit
2
+ module ChildProcess
3
+ module Unix
4
+ class ForkExecProcess < Process
5
+ private
6
+
7
+ def launch_process
8
+ if @io
9
+ stdout = @io.stdout
10
+ stderr = @io.stderr
11
+ end
12
+
13
+ # pipe used to detect exec() failure
14
+ exec_r, exec_w = ::IO.pipe
15
+ ChildProcess.close_on_exec exec_w
16
+
17
+ if duplex?
18
+ reader, writer = ::IO.pipe
19
+ end
20
+
21
+ @pid = Kernel.fork {
22
+ # Children of the forked process will inherit its process group
23
+ # This is to make sure that all grandchildren dies when this Process instance is killed
24
+ ::Process.setpgid 0, 0 if leader?
25
+
26
+ if @cwd
27
+ Dir.chdir(@cwd)
28
+ end
29
+
30
+ exec_r.close
31
+ set_env
32
+
33
+ STDOUT.reopen(stdout || "/dev/null")
34
+ STDERR.reopen(stderr || "/dev/null")
35
+
36
+ if duplex?
37
+ STDIN.reopen(reader)
38
+ writer.close
39
+ end
40
+
41
+ executable, *args = @args
42
+
43
+ begin
44
+ Kernel.exec([executable, executable], *args)
45
+ rescue SystemCallError => ex
46
+ exec_w << ex.message
47
+ end
48
+ }
49
+
50
+ exec_w.close
51
+
52
+ if duplex?
53
+ io._stdin = writer
54
+ reader.close
55
+ end
56
+
57
+ # if we don't eventually get EOF, exec() failed
58
+ unless exec_r.eof?
59
+ raise LaunchError, exec_r.read || "executing command with #{@args.inspect} failed"
60
+ end
61
+
62
+ ::Process.detach(@pid) if detach?
63
+ end
64
+
65
+ def set_env
66
+ @environment.each { |k, v| ENV[k.to_s] = v.nil? ? nil : v.to_s }
67
+ end
68
+
69
+ end # Process
70
+ end # Unix
71
+ end # ChildProcess
72
+ end
@@ -0,0 +1,22 @@
1
+ module DockerToolkit
2
+ module ChildProcess
3
+ module Unix
4
+ class IO < AbstractIO
5
+ private
6
+
7
+ def check_type(io)
8
+ unless io.respond_to? :to_io
9
+ raise ArgumentError, "expected #{io.inspect} to respond to :to_io"
10
+ end
11
+
12
+ result = io.to_io
13
+ unless result && result.kind_of?(::IO)
14
+ raise TypeError, "expected IO, got #{result.inspect}:#{result.class}"
15
+ end
16
+ end
17
+
18
+ end # IO
19
+ end # Unix
20
+ end # ChildProcess
21
+ end
22
+
@@ -0,0 +1,188 @@
1
+ module DockerToolkit
2
+ module ChildProcess
3
+ module Unix
4
+ module Lib
5
+ extend FFI::Library
6
+ ffi_lib FFI::Library::LIBC
7
+
8
+ if ChildProcess.os == :macosx
9
+ attach_function :_NSGetEnviron, [], :pointer
10
+ def self.environ
11
+ _NSGetEnviron().read_pointer
12
+ end
13
+ elsif respond_to? :attach_variable
14
+ attach_variable :environ, :pointer
15
+ end
16
+
17
+ attach_function :strerror, [:int], :string
18
+ attach_function :chdir, [:string], :int
19
+ attach_function :fcntl, [:int, :int, :int], :int # fcntl actually takes varags, but we only need this version.
20
+
21
+ # int posix_spawnp(
22
+ # pid_t *restrict pid,
23
+ # const char *restrict file,
24
+ # const posix_spawn_file_actions_t *file_actions,
25
+ # const posix_spawnattr_t *restrict attrp,
26
+ # char *const argv[restrict],
27
+ # char *const envp[restrict]
28
+ # );
29
+
30
+ attach_function :posix_spawnp, [
31
+ :pointer,
32
+ :string,
33
+ :pointer,
34
+ :pointer,
35
+ :pointer,
36
+ :pointer
37
+ ], :int
38
+
39
+ # int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
40
+ attach_function :posix_spawn_file_actions_init, [:pointer], :int
41
+
42
+ # int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
43
+ attach_function :posix_spawn_file_actions_destroy, [:pointer], :int
44
+
45
+ # int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, int filedes);
46
+ attach_function :posix_spawn_file_actions_addclose, [:pointer, :int], :int
47
+
48
+ # int posix_spawn_file_actions_addopen(
49
+ # posix_spawn_file_actions_t *restrict file_actions,
50
+ # int filedes,
51
+ # const char *restrict path,
52
+ # int oflag,
53
+ # mode_t mode
54
+ # );
55
+ attach_function :posix_spawn_file_actions_addopen, [:pointer, :int, :string, :int, :mode_t], :int
56
+
57
+ # int posix_spawn_file_actions_adddup2(
58
+ # posix_spawn_file_actions_t *file_actions,
59
+ # int filedes,
60
+ # int newfiledes
61
+ # );
62
+ attach_function :posix_spawn_file_actions_adddup2, [:pointer, :int, :int], :int
63
+
64
+ # int posix_spawnattr_init(posix_spawnattr_t *attr);
65
+ attach_function :posix_spawnattr_init, [:pointer], :int
66
+
67
+ # int posix_spawnattr_destroy(posix_spawnattr_t *attr);
68
+ attach_function :posix_spawnattr_destroy, [:pointer], :int
69
+
70
+ # int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags);
71
+ attach_function :posix_spawnattr_setflags, [:pointer, :short], :int
72
+
73
+ # int posix_spawnattr_getflags(const posix_spawnattr_t *restrict attr, short *restrict flags);
74
+ attach_function :posix_spawnattr_getflags, [:pointer, :pointer], :int
75
+
76
+ # int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup);
77
+ attach_function :posix_spawnattr_setpgroup, [:pointer, :pid_t], :int
78
+
79
+ # int posix_spawnattr_getpgroup(const posix_spawnattr_t *restrict attr, pid_t *restrict pgroup);
80
+ attach_function :posix_spawnattr_getpgroup, [:pointer, :pointer], :int
81
+
82
+ # int posix_spawnattr_setsigdefault(posix_spawnattr_t *restrict attr, const sigset_t *restrict sigdefault);
83
+ attach_function :posix_spawnattr_setsigdefault, [:pointer, :pointer], :int
84
+
85
+ # int posix_spawnattr_getsigdefault(const posix_spawnattr_t *restrict attr, sigset_t *restrict sigdefault);
86
+ attach_function :posix_spawnattr_getsigdefault, [:pointer, :pointer], :int
87
+
88
+ # int posix_spawnattr_setsigmask(posix_spawnattr_t *restrict attr, const sigset_t *restrict sigmask);
89
+ attach_function :posix_spawnattr_setsigmask, [:pointer, :pointer], :int
90
+
91
+ # int posix_spawnattr_getsigmask(const posix_spawnattr_t *restrict attr, sigset_t *restrict sigmask);
92
+ attach_function :posix_spawnattr_getsigmask, [:pointer, :pointer], :int
93
+
94
+ def self.check(errno)
95
+ if errno != 0
96
+ raise Error, Lib.strerror(FFI.errno)
97
+ end
98
+ end
99
+
100
+ class FileActions
101
+ def initialize
102
+ @ptr = FFI::MemoryPointer.new(1, Platform::SIZEOF.fetch(:posix_spawn_file_actions_t), false)
103
+ Lib.check Lib.posix_spawn_file_actions_init(@ptr)
104
+ end
105
+
106
+ def add_close(fileno)
107
+ Lib.check Lib.posix_spawn_file_actions_addclose(
108
+ @ptr,
109
+ fileno
110
+ )
111
+ end
112
+
113
+ def add_open(fileno, path, oflag, mode)
114
+ Lib.check Lib.posix_spawn_file_actions_addopen(
115
+ @ptr,
116
+ fileno,
117
+ path,
118
+ oflag,
119
+ mode
120
+ )
121
+ end
122
+
123
+ def add_dup(fileno, new_fileno)
124
+ Lib.check Lib.posix_spawn_file_actions_adddup2(
125
+ @ptr,
126
+ fileno,
127
+ new_fileno
128
+ )
129
+ end
130
+
131
+ def free
132
+ Lib.check Lib.posix_spawn_file_actions_destroy(@ptr)
133
+ @ptr = nil
134
+ end
135
+
136
+ def to_ptr
137
+ @ptr
138
+ end
139
+ end # FileActions
140
+
141
+ class Attrs
142
+ def initialize
143
+ @ptr = FFI::MemoryPointer.new(1, Platform::SIZEOF.fetch(:posix_spawnattr_t), false)
144
+ Lib.check Lib.posix_spawnattr_init(@ptr)
145
+ end
146
+
147
+ def free
148
+ Lib.check Lib.posix_spawnattr_destroy(@ptr)
149
+ @ptr = nil
150
+ end
151
+
152
+ def flags=(flags)
153
+ Lib.check Lib.posix_spawnattr_setflags(@ptr, flags)
154
+ end
155
+
156
+ def flags
157
+ ptr = FFI::MemoryPointer.new(:short)
158
+ Lib.check Lib.posix_spawnattr_getflags(@ptr, ptr)
159
+
160
+ ptr.read_short
161
+ end
162
+
163
+ def pgroup=(pid)
164
+ self.flags |= Platform::POSIX_SPAWN_SETPGROUP
165
+ Lib.check Lib.posix_spawnattr_setpgroup(@ptr, pid)
166
+ end
167
+
168
+ def to_ptr
169
+ @ptr
170
+ end
171
+ end # Attrs
172
+
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ # missing on rubinius
179
+ class FFI::MemoryPointer
180
+ unless method_defined?(:from_string)
181
+ def self.from_string(str)
182
+ ptr = new(1, str.bytesize + 1)
183
+ ptr.write_string("#{str}\0")
184
+
185
+ ptr
186
+ end
187
+ end
188
+ end if defined?(FFI)
@@ -0,0 +1,14 @@
1
+ module DockerToolkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 76,
5
+ :posix_spawnattr_t => 336,
6
+ :sigset_t => 128
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ POSIX_SPAWN_USEVFORK = 64
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module DockerToolkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 4,
5
+ :posix_spawnattr_t => 4,
6
+ :sigset_t => 16
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module DockerToolkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 80,
5
+ :posix_spawnattr_t => 336,
6
+ :sigset_t => 128
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ POSIX_SPAWN_USEVFORK = 64
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module DockerToolkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 8,
5
+ :posix_spawnattr_t => 8,
6
+ :sigset_t => 4
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ end
13
+ end
@@ -0,0 +1,135 @@
1
+ require 'thread'
2
+
3
+ module DockerToolkit
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
135
+ end