aggkit 0.2.5
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 +7 -0
- data/.dockerignore +20 -0
- data/.gitignore +109 -0
- data/.gitlab-ci.yml +66 -0
- data/.rspec +4 -0
- data/.rubocop.yml +98 -0
- data/.travis.yml +30 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +55 -0
- data/README.md +96 -0
- data/aggkit.gemspec +38 -0
- data/bin/agg +167 -0
- data/bin/aggconsul +222 -0
- data/bin/agglock +71 -0
- data/bin/aggmerge +118 -0
- data/bin/aggwait +262 -0
- data/bin/consul.rb +222 -0
- data/bin/locker.rb +71 -0
- data/bin/merger.rb +118 -0
- data/bin/terminator.rb +71 -0
- data/bin/waiter.rb +262 -0
- data/docker/Dockerfile +112 -0
- data/docker/docker-compose.yml +12 -0
- data/docker/down.sh +4 -0
- data/docker/run_tests.sh +23 -0
- data/lib/aggkit/childprocess/abstract_io.rb +38 -0
- data/lib/aggkit/childprocess/abstract_process.rb +194 -0
- data/lib/aggkit/childprocess/errors.rb +28 -0
- data/lib/aggkit/childprocess/jruby/io.rb +17 -0
- data/lib/aggkit/childprocess/jruby/process.rb +161 -0
- data/lib/aggkit/childprocess/jruby/pump.rb +55 -0
- data/lib/aggkit/childprocess/jruby.rb +58 -0
- data/lib/aggkit/childprocess/tools/generator.rb +148 -0
- data/lib/aggkit/childprocess/unix/fork_exec_process.rb +72 -0
- data/lib/aggkit/childprocess/unix/io.rb +22 -0
- data/lib/aggkit/childprocess/unix/lib.rb +188 -0
- data/lib/aggkit/childprocess/unix/platform/i386-linux.rb +14 -0
- data/lib/aggkit/childprocess/unix/platform/i386-solaris.rb +13 -0
- data/lib/aggkit/childprocess/unix/platform/x86_64-linux.rb +14 -0
- data/lib/aggkit/childprocess/unix/platform/x86_64-macosx.rb +13 -0
- data/lib/aggkit/childprocess/unix/posix_spawn_process.rb +135 -0
- data/lib/aggkit/childprocess/unix/process.rb +91 -0
- data/lib/aggkit/childprocess/unix.rb +11 -0
- data/lib/aggkit/childprocess/version.rb +5 -0
- data/lib/aggkit/childprocess/windows/handle.rb +93 -0
- data/lib/aggkit/childprocess/windows/io.rb +25 -0
- data/lib/aggkit/childprocess/windows/lib.rb +418 -0
- data/lib/aggkit/childprocess/windows/process.rb +132 -0
- data/lib/aggkit/childprocess/windows/process_builder.rb +177 -0
- data/lib/aggkit/childprocess/windows/structs.rb +151 -0
- data/lib/aggkit/childprocess/windows.rb +35 -0
- data/lib/aggkit/childprocess.rb +213 -0
- data/lib/aggkit/env.rb +219 -0
- data/lib/aggkit/runner.rb +80 -0
- data/lib/aggkit/version.rb +5 -0
- data/lib/aggkit/watcher.rb +239 -0
- data/lib/aggkit.rb +15 -0
- metadata +196 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
require "java"
|
2
|
+
|
3
|
+
module Aggkit
|
4
|
+
module ChildProcess
|
5
|
+
module JRuby
|
6
|
+
class Process < AbstractProcess
|
7
|
+
def initialize(args)
|
8
|
+
super(args)
|
9
|
+
|
10
|
+
@pumps = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def io
|
14
|
+
@io ||= JRuby::IO.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def exited?
|
18
|
+
return true if @exit_code
|
19
|
+
|
20
|
+
assert_started
|
21
|
+
@exit_code = @process.exitValue
|
22
|
+
stop_pumps
|
23
|
+
|
24
|
+
true
|
25
|
+
rescue java.lang.IllegalThreadStateException => ex
|
26
|
+
log(ex.class => ex.message)
|
27
|
+
false
|
28
|
+
ensure
|
29
|
+
log(:exit_code => @exit_code)
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop(timeout = nil)
|
33
|
+
assert_started
|
34
|
+
|
35
|
+
@process.destroy
|
36
|
+
wait # no way to actually use the timeout here..
|
37
|
+
end
|
38
|
+
|
39
|
+
def wait
|
40
|
+
if exited?
|
41
|
+
exit_code
|
42
|
+
else
|
43
|
+
@process.waitFor
|
44
|
+
|
45
|
+
stop_pumps
|
46
|
+
@exit_code = @process.exitValue
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Only supported in JRuby on a Unix operating system, thanks to limitations
|
52
|
+
# in Java's classes
|
53
|
+
#
|
54
|
+
# @return [Integer] the pid of the process after it has started
|
55
|
+
# @raise [NotImplementedError] when trying to access pid on non-Unix platform
|
56
|
+
#
|
57
|
+
def pid
|
58
|
+
if @process.getClass.getName != "java.lang.UNIXProcess"
|
59
|
+
raise NotImplementedError, "pid is only supported by JRuby child processes on Unix"
|
60
|
+
end
|
61
|
+
|
62
|
+
# About the best way we can do this is with a nasty reflection-based impl
|
63
|
+
# Thanks to Martijn Courteaux
|
64
|
+
# http://stackoverflow.com/questions/2950338/how-can-i-kill-a-linux-process-in-java-with-sigkill-process-destroy-does-sigter/2951193#2951193
|
65
|
+
field = @process.getClass.getDeclaredField("pid")
|
66
|
+
field.accessible = true
|
67
|
+
field.get(@process)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def launch_process(&blk)
|
73
|
+
pb = java.lang.ProcessBuilder.new(@args)
|
74
|
+
|
75
|
+
pb.directory java.io.File.new(@cwd || Dir.pwd)
|
76
|
+
set_env pb.environment
|
77
|
+
|
78
|
+
begin
|
79
|
+
@process = pb.start
|
80
|
+
rescue java.io.IOException => ex
|
81
|
+
raise LaunchError, ex.message
|
82
|
+
end
|
83
|
+
|
84
|
+
setup_io
|
85
|
+
end
|
86
|
+
|
87
|
+
def setup_io
|
88
|
+
if @io
|
89
|
+
redirect(@process.getErrorStream, @io.stderr)
|
90
|
+
redirect(@process.getInputStream, @io.stdout)
|
91
|
+
else
|
92
|
+
@process.getErrorStream.close
|
93
|
+
@process.getInputStream.close
|
94
|
+
end
|
95
|
+
|
96
|
+
if duplex?
|
97
|
+
io._stdin = create_stdin
|
98
|
+
else
|
99
|
+
@process.getOutputStream.close
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def redirect(input, output)
|
104
|
+
if output.nil?
|
105
|
+
input.close
|
106
|
+
return
|
107
|
+
end
|
108
|
+
|
109
|
+
@pumps << Pump.new(input, output.to_outputstream).run
|
110
|
+
end
|
111
|
+
|
112
|
+
def stop_pumps
|
113
|
+
@pumps.each { |pump| pump.stop }
|
114
|
+
end
|
115
|
+
|
116
|
+
def set_env(env)
|
117
|
+
merged = ENV.to_hash
|
118
|
+
|
119
|
+
@environment.each { |k, v| merged[k.to_s] = v }
|
120
|
+
|
121
|
+
merged.each do |k, v|
|
122
|
+
if v
|
123
|
+
env.put(k, v.to_s)
|
124
|
+
elsif env.has_key? k
|
125
|
+
env.remove(k)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
removed_keys = env.key_set.to_a - merged.keys
|
130
|
+
removed_keys.each { |k| env.remove(k) }
|
131
|
+
end
|
132
|
+
|
133
|
+
def create_stdin
|
134
|
+
output_stream = @process.getOutputStream
|
135
|
+
|
136
|
+
stdin = output_stream.to_io
|
137
|
+
stdin.sync = true
|
138
|
+
stdin.instance_variable_set(:@childprocess_java_stream, output_stream)
|
139
|
+
|
140
|
+
class << stdin
|
141
|
+
# The stream provided is a BufferedeOutputStream, so we
|
142
|
+
# have to flush it to make the bytes flow to the process
|
143
|
+
def __childprocess_flush__
|
144
|
+
@childprocess_java_stream.flush
|
145
|
+
end
|
146
|
+
|
147
|
+
[:flush, :print, :printf, :putc, :puts, :write, :write_nonblock].each do |m|
|
148
|
+
define_method(m) do |*args|
|
149
|
+
super(*args)
|
150
|
+
self.__childprocess_flush__
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
stdin
|
156
|
+
end
|
157
|
+
|
158
|
+
end # Process
|
159
|
+
end # JRuby
|
160
|
+
end # ChildProcess
|
161
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Aggkit
|
2
|
+
module ChildProcess
|
3
|
+
module JRuby
|
4
|
+
class Pump
|
5
|
+
BUFFER_SIZE = 2048
|
6
|
+
|
7
|
+
def initialize(input, output)
|
8
|
+
@input = input
|
9
|
+
@output = output
|
10
|
+
@stop = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def stop
|
14
|
+
@stop = true
|
15
|
+
@thread && @thread.join
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
@thread = Thread.new { pump }
|
20
|
+
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def pump
|
27
|
+
buffer = Java.byte[BUFFER_SIZE].new
|
28
|
+
|
29
|
+
until @stop && (@input.available == 0)
|
30
|
+
read, avail = 0, 0
|
31
|
+
|
32
|
+
while read != -1
|
33
|
+
avail = [@input.available, 1].max
|
34
|
+
avail = BUFFER_SIZE if avail > BUFFER_SIZE
|
35
|
+
read = @input.read(buffer, 0, avail)
|
36
|
+
|
37
|
+
if read > 0
|
38
|
+
@output.write(buffer, 0, read)
|
39
|
+
@output.flush
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
sleep 0.1
|
44
|
+
end
|
45
|
+
|
46
|
+
@output.flush
|
47
|
+
rescue java.io.IOException => ex
|
48
|
+
ChildProcess.logger.debug ex.message
|
49
|
+
ChildProcess.logger.debug ex.backtrace
|
50
|
+
end
|
51
|
+
|
52
|
+
end # Pump
|
53
|
+
end # JRuby
|
54
|
+
end # ChildProcess
|
55
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'jruby'
|
3
|
+
|
4
|
+
class Java::SunNioCh::FileChannelImpl
|
5
|
+
field_reader :fd
|
6
|
+
end
|
7
|
+
|
8
|
+
class Java::JavaIo::FileDescriptor
|
9
|
+
if ChildProcess.os == :windows
|
10
|
+
field_reader :handle
|
11
|
+
end
|
12
|
+
|
13
|
+
field_reader :fd
|
14
|
+
end
|
15
|
+
|
16
|
+
module Aggkit
|
17
|
+
module ChildProcess
|
18
|
+
module JRuby
|
19
|
+
def self.posix_fileno_for(obj)
|
20
|
+
channel = ::JRuby.reference(obj).channel
|
21
|
+
begin
|
22
|
+
channel.getFDVal
|
23
|
+
rescue NoMethodError
|
24
|
+
fileno = channel.fd
|
25
|
+
if fileno.kind_of?(Java::JavaIo::FileDescriptor)
|
26
|
+
fileno = fileno.fd
|
27
|
+
end
|
28
|
+
|
29
|
+
fileno == -1 ? obj.fileno : fileno
|
30
|
+
end
|
31
|
+
rescue
|
32
|
+
# fall back
|
33
|
+
obj.fileno
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.windows_handle_for(obj)
|
37
|
+
channel = ::JRuby.reference(obj).channel
|
38
|
+
fileno = obj.fileno
|
39
|
+
|
40
|
+
begin
|
41
|
+
fileno = channel.getFDVal
|
42
|
+
rescue NoMethodError
|
43
|
+
fileno = channel.fd if channel.respond_to?(:fd)
|
44
|
+
end
|
45
|
+
|
46
|
+
if fileno.kind_of? Java::JavaIo::FileDescriptor
|
47
|
+
fileno.handle
|
48
|
+
else
|
49
|
+
Windows::Lib.handle_for fileno
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
require "aggkit/childprocess/jruby/pump"
|
57
|
+
require "aggkit/childprocess/jruby/io"
|
58
|
+
require "aggkit/childprocess/jruby/process"
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Aggkit
|
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 Aggkit
|
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 Aggkit
|
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 Aggkit
|
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)
|