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)
         |