From f0b85199e5fe08334988f02f73580131d23c22a5 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 16 Feb 2021 13:36:06 +0100 Subject: [PATCH 1/5] Try latest JRuby --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e1bae59..6cdbb27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,6 @@ matrix: - rvm: ruby-head - env: "CHILDPROCESS_POSIX_SPAWN=true" include: - - rvm: jruby-9.2.5.0 + - rvm: jruby jdk: openjdk11 env: "JAVA_OPTS_FOR_SPECS='--add-opens java.base/java.io=org.jruby.dist --add-opens java.base/sun.nio.ch=org.jruby.dist'" From 743ed3e3c181ac88514d599e438d4302b8646a5b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Dec 2020 20:49:51 +0100 Subject: [PATCH 2/5] WIP --- lib/childprocess.rb | 31 ++++++------ .../unix/process_spawn_process.rb | 47 +++++++++++++++++++ 2 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 lib/childprocess/unix/process_spawn_process.rb diff --git a/lib/childprocess.rb b/lib/childprocess.rb index 09cc132..4642fe9 100644 --- a/lib/childprocess.rb +++ b/lib/childprocess.rb @@ -13,20 +13,23 @@ class << self attr_writer :logger def new(*args) - case os - when :macosx, :linux, :solaris, :bsd, :cygwin, :aix - if posix_spawn? - Unix::PosixSpawnProcess.new(args) - elsif jruby? - JRuby::Process.new(args) - else - Unix::ForkExecProcess.new(args) - end - when :windows - Windows::Process.new(args) - else - raise Error, "unsupported platform #{platform_name.inspect}" - end + require 'childprocess/unix/process_spawn_process' + return Unix::PosixSpawnProcess.new(args) + + # case os + # when :macosx, :linux, :solaris, :bsd, :cygwin, :aix + # if posix_spawn? + # Unix::PosixSpawnProcess.new(args) + # elsif jruby? + # JRuby::Process.new(args) + # else + # Unix::ForkExecProcess.new(args) + # end + # when :windows + # Windows::Process.new(args) + # else + # raise Error, "unsupported platform #{platform_name.inspect}" + # end end alias_method :build, :new diff --git a/lib/childprocess/unix/process_spawn_process.rb b/lib/childprocess/unix/process_spawn_process.rb new file mode 100644 index 0000000..a7e97f1 --- /dev/null +++ b/lib/childprocess/unix/process_spawn_process.rb @@ -0,0 +1,47 @@ +module ChildProcess + module Unix + class PosixSpawnProcess < Process + private + + def launch_process + options = {} + + options[:out] = io.stdout ? io.stdout.fileno : File::NULL + options[:err] = io.stderr ? io.stderr.fileno : File::NULL + + if duplex? + reader, writer = ::IO.pipe + options[:in] = reader.fileno + options[writer.fileno] = :close + end + + options[:pgroup] = true if leader? + + options[:chdir] = @cwd if @cwd + + if @args.size == 1 + # When given a single String, Process.spawn would think it should use the shell + # if there is any special character in it. However, ChildProcess should never + # use the shell. So we use the [cmdname, argv0] form to force no shell. + arg = @args[0] + args = [[arg, arg]] + else + args = @args + end + + begin + @pid = ::Process.spawn(@environment, *args, options) + rescue SystemCallError => e + raise LaunchError, e.message + end + + if duplex? + io._stdin = writer + reader.close + end + + ::Process.detach(@pid) if detach? + end + end + end +end From e32d8c2d4f00179cc720c73912ca431cf10f4e21 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 1 Mar 2021 00:53:13 +0100 Subject: [PATCH 3/5] Simplify --- lib/childprocess.rb | 32 +-- lib/childprocess/jruby.rb | 56 ------ lib/childprocess/jruby/io.rb | 16 -- lib/childprocess/jruby/process.rb | 184 ----------------- lib/childprocess/jruby/pump.rb | 53 ----- lib/childprocess/unix.rb | 9 - lib/childprocess/unix/fork_exec_process.rb | 78 -------- lib/childprocess/unix/lib.rb | 186 ------------------ lib/childprocess/unix/platform/i386-linux.rb | 12 -- .../unix/platform/i386-solaris.rb | 11 -- .../unix/platform/x86_64-linux.rb | 12 -- .../unix/platform/x86_64-macosx.rb | 11 -- lib/childprocess/unix/posix_spawn_process.rb | 134 ------------- .../unix/process_spawn_process.rb | 3 + 14 files changed, 13 insertions(+), 784 deletions(-) delete mode 100644 lib/childprocess/jruby.rb delete mode 100644 lib/childprocess/jruby/io.rb delete mode 100755 lib/childprocess/jruby/process.rb delete mode 100644 lib/childprocess/jruby/pump.rb delete mode 100644 lib/childprocess/unix.rb delete mode 100644 lib/childprocess/unix/fork_exec_process.rb delete mode 100644 lib/childprocess/unix/lib.rb delete mode 100644 lib/childprocess/unix/platform/i386-linux.rb delete mode 100644 lib/childprocess/unix/platform/i386-solaris.rb delete mode 100644 lib/childprocess/unix/platform/x86_64-linux.rb delete mode 100644 lib/childprocess/unix/platform/x86_64-macosx.rb delete mode 100644 lib/childprocess/unix/posix_spawn_process.rb diff --git a/lib/childprocess.rb b/lib/childprocess.rb index 4642fe9..5f3e0f9 100644 --- a/lib/childprocess.rb +++ b/lib/childprocess.rb @@ -5,6 +5,8 @@ require "fcntl" require 'logger' +require 'childprocess/unix/process_spawn_process' + module ChildProcess @posix_spawn = false @@ -13,23 +15,14 @@ class << self attr_writer :logger def new(*args) - require 'childprocess/unix/process_spawn_process' - return Unix::PosixSpawnProcess.new(args) - - # case os - # when :macosx, :linux, :solaris, :bsd, :cygwin, :aix - # if posix_spawn? - # Unix::PosixSpawnProcess.new(args) - # elsif jruby? - # JRuby::Process.new(args) - # else - # Unix::ForkExecProcess.new(args) - # end - # when :windows - # Windows::Process.new(args) - # else - # raise Error, "unsupported platform #{platform_name.inspect}" - # end + case os + when :macosx, :linux, :solaris, :bsd, :cygwin, :aix + Unix::PosixSpawnProcess.new(args) + when :windows + Windows::Process.new(args) + else + raise Error, "unsupported platform #{platform_name.inspect}" + end end alias_method :build, :new @@ -88,9 +81,6 @@ def posix_spawn? raise ChildProcess::MissingPlatformError end - require "childprocess/unix/lib" - require 'childprocess/unix/posix_spawn_process' - true rescue ChildProcess::MissingPlatformError => ex warn_once ex.message @@ -208,6 +198,4 @@ def is_64_bit? require 'jruby' if ChildProcess.jruby? -require 'childprocess/unix' if ChildProcess.unix? require 'childprocess/windows' if ChildProcess.windows? -require 'childprocess/jruby' if ChildProcess.jruby? diff --git a/lib/childprocess/jruby.rb b/lib/childprocess/jruby.rb deleted file mode 100644 index f8bbc02..0000000 --- a/lib/childprocess/jruby.rb +++ /dev/null @@ -1,56 +0,0 @@ -require 'java' -require 'jruby' - -class Java::SunNioCh::FileChannelImpl - field_reader :fd -end - -class Java::JavaIo::FileDescriptor - if ChildProcess.os == :windows - field_reader :handle - end - - field_reader :fd -end - -module ChildProcess - module JRuby - def self.posix_fileno_for(obj) - channel = ::JRuby.reference(obj).channel - begin - channel.getFDVal - rescue NoMethodError - fileno = channel.fd - if fileno.kind_of?(Java::JavaIo::FileDescriptor) - fileno = fileno.fd - end - - fileno == -1 ? obj.fileno : fileno - end - rescue - # fall back - obj.fileno - end - - def self.windows_handle_for(obj) - channel = ::JRuby.reference(obj).channel - fileno = obj.fileno - - begin - fileno = channel.getFDVal - rescue NoMethodError - fileno = channel.fd if channel.respond_to?(:fd) - end - - if fileno.kind_of? Java::JavaIo::FileDescriptor - fileno.handle - else - Windows::Lib.handle_for fileno - end - end - end -end - -require "childprocess/jruby/pump" -require "childprocess/jruby/io" -require "childprocess/jruby/process" diff --git a/lib/childprocess/jruby/io.rb b/lib/childprocess/jruby/io.rb deleted file mode 100644 index f4e8c2b..0000000 --- a/lib/childprocess/jruby/io.rb +++ /dev/null @@ -1,16 +0,0 @@ -module ChildProcess - module JRuby - class IO < AbstractIO - private - - def check_type(output) - unless output.respond_to?(:to_outputstream) && output.respond_to?(:write) - raise ArgumentError, "expected #{output.inspect} to respond to :to_outputstream" - end - end - - end # IO - end # Unix -end # ChildProcess - - diff --git a/lib/childprocess/jruby/process.rb b/lib/childprocess/jruby/process.rb deleted file mode 100755 index cd80fad..0000000 --- a/lib/childprocess/jruby/process.rb +++ /dev/null @@ -1,184 +0,0 @@ -require "java" - -module ChildProcess - module JRuby - class Process < AbstractProcess - def initialize(args) - super(args) - - @pumps = [] - end - - def io - @io ||= JRuby::IO.new - end - - def exited? - return true if @exit_code - - assert_started - @exit_code = @process.exitValue - stop_pumps - - true - rescue java.lang.IllegalThreadStateException => ex - log(ex.class => ex.message) - false - ensure - log(:exit_code => @exit_code) - end - - def stop(timeout = nil) - assert_started - - @process.destroy - wait # no way to actually use the timeout here.. - end - - def wait - if exited? - exit_code - else - @process.waitFor - - stop_pumps - @exit_code = @process.exitValue - end - end - - # Implementation of ChildProcess::JRuby::Process#pid depends heavily on - # what Java SDK is being used; here, we look it up once at load, then - # define the method once to avoid runtime overhead. - normalised_java_version_major = java.lang.System.get_property("java.version") - .slice(/^(1\.)?([0-9]+)/, 2) - .to_i - if normalised_java_version_major >= 9 - - # On modern Javas, we can simply delegate through to `Process#pid`, - # which was introduced in Java 9. - # - # @return [Integer] the pid of the process after it has started - # @raise [NotImplementedError] when trying to access pid on platform for - # which it is unsupported in Java - def pid - @process.pid - rescue java.lang.UnsupportedOperationException => e - raise NotImplementedError, "pid is not supported on this platform: #{e.message}" - end - - else - - # On Legacy Javas, fall back to reflection. - # - # Only supported in JRuby on a Unix operating system, thanks to limitations - # in Java's classes - # - # @return [Integer] the pid of the process after it has started - # @raise [NotImplementedError] when trying to access pid on non-Unix platform - # - def pid - if @process.getClass.getName != "java.lang.UNIXProcess" - raise NotImplementedError, "pid is only supported by JRuby child processes on Unix" - end - - # About the best way we can do this is with a nasty reflection-based impl - # Thanks to Martijn Courteaux - # http://stackoverflow.com/questions/2950338/how-can-i-kill-a-linux-process-in-java-with-sigkill-process-destroy-does-sigter/2951193#2951193 - field = @process.getClass.getDeclaredField("pid") - field.accessible = true - field.get(@process) - end - - end - - private - - def launch_process(&blk) - pb = java.lang.ProcessBuilder.new(@args) - - pb.directory java.io.File.new(@cwd || Dir.pwd) - set_env pb.environment - - begin - @process = pb.start - rescue java.io.IOException => ex - raise LaunchError, ex.message - end - - setup_io - end - - def setup_io - if @io - redirect(@process.getErrorStream, @io.stderr) - redirect(@process.getInputStream, @io.stdout) - else - @process.getErrorStream.close - @process.getInputStream.close - end - - if duplex? - io._stdin = create_stdin - else - @process.getOutputStream.close - end - end - - def redirect(input, output) - if output.nil? - input.close - return - end - - @pumps << Pump.new(input, output.to_outputstream).run - end - - def stop_pumps - @pumps.each { |pump| pump.stop } - end - - def set_env(env) - merged = ENV.to_hash - - @environment.each { |k, v| merged[k.to_s] = v } - - merged.each do |k, v| - if v - env.put(k, v.to_s) - elsif env.has_key? k - env.remove(k) - end - end - - removed_keys = env.key_set.to_a - merged.keys - removed_keys.each { |k| env.remove(k) } - end - - def create_stdin - output_stream = @process.getOutputStream - - stdin = output_stream.to_io - stdin.sync = true - stdin.instance_variable_set(:@childprocess_java_stream, output_stream) - - class << stdin - # The stream provided is a BufferedeOutputStream, so we - # have to flush it to make the bytes flow to the process - def __childprocess_flush__ - @childprocess_java_stream.flush - end - - [:flush, :print, :printf, :putc, :puts, :write, :write_nonblock].each do |m| - define_method(m) do |*args| - super(*args) - self.__childprocess_flush__ - end - end - end - - stdin - end - - end # Process - end # JRuby -end # ChildProcess diff --git a/lib/childprocess/jruby/pump.rb b/lib/childprocess/jruby/pump.rb deleted file mode 100644 index 64ac32d..0000000 --- a/lib/childprocess/jruby/pump.rb +++ /dev/null @@ -1,53 +0,0 @@ -module ChildProcess - module JRuby - class Pump - BUFFER_SIZE = 2048 - - def initialize(input, output) - @input = input - @output = output - @stop = false - end - - def stop - @stop = true - @thread && @thread.join - end - - def run - @thread = Thread.new { pump } - - self - end - - private - - def pump - buffer = Java.byte[BUFFER_SIZE].new - - until @stop && (@input.available == 0) - read, avail = 0, 0 - - while read != -1 - avail = [@input.available, 1].max - avail = BUFFER_SIZE if avail > BUFFER_SIZE - read = @input.read(buffer, 0, avail) - - if read > 0 - @output.write(buffer, 0, read) - @output.flush - end - end - - sleep 0.1 - end - - @output.flush - rescue java.io.IOException => ex - ChildProcess.logger.debug ex.message - ChildProcess.logger.debug ex.backtrace - end - - end # Pump - end # JRuby -end # ChildProcess diff --git a/lib/childprocess/unix.rb b/lib/childprocess/unix.rb deleted file mode 100644 index 09a8054..0000000 --- a/lib/childprocess/unix.rb +++ /dev/null @@ -1,9 +0,0 @@ -module ChildProcess - module Unix - end -end - -require "childprocess/unix/io" -require "childprocess/unix/process" -require "childprocess/unix/fork_exec_process" -# PosixSpawnProcess + ffi is required on demand. diff --git a/lib/childprocess/unix/fork_exec_process.rb b/lib/childprocess/unix/fork_exec_process.rb deleted file mode 100644 index cc7a850..0000000 --- a/lib/childprocess/unix/fork_exec_process.rb +++ /dev/null @@ -1,78 +0,0 @@ -module ChildProcess - module Unix - class ForkExecProcess < Process - private - - def launch_process - if @io - stdout = @io.stdout - stderr = @io.stderr - end - - # pipe used to detect exec() failure - exec_r, exec_w = ::IO.pipe - ChildProcess.close_on_exec exec_w - - if duplex? - reader, writer = ::IO.pipe - end - - @pid = Kernel.fork { - # Children of the forked process will inherit its process group - # This is to make sure that all grandchildren dies when this Process instance is killed - ::Process.setpgid 0, 0 if leader? - - if @cwd - Dir.chdir(@cwd) - end - - exec_r.close - set_env - - if stdout - STDOUT.reopen(stdout) - else - STDOUT.reopen("/dev/null", "a+") - end - if stderr - STDERR.reopen(stderr) - else - STDERR.reopen("/dev/null", "a+") - end - - if duplex? - STDIN.reopen(reader) - writer.close - end - - executable, *args = @args - - begin - Kernel.exec([executable, executable], *args) - rescue SystemCallError => ex - exec_w << ex.message - end - } - - exec_w.close - - if duplex? - io._stdin = writer - reader.close - end - - # if we don't eventually get EOF, exec() failed - unless exec_r.eof? - raise LaunchError, exec_r.read || "executing command with #{@args.inspect} failed" - end - - ::Process.detach(@pid) if detach? - end - - def set_env - @environment.each { |k, v| ENV[k.to_s] = v.nil? ? nil : v.to_s } - end - - end # Process - end # Unix -end # ChildProcess diff --git a/lib/childprocess/unix/lib.rb b/lib/childprocess/unix/lib.rb deleted file mode 100644 index 22f19f2..0000000 --- a/lib/childprocess/unix/lib.rb +++ /dev/null @@ -1,186 +0,0 @@ -module ChildProcess - module Unix - module Lib - extend FFI::Library - ffi_lib FFI::Library::LIBC - - if ChildProcess.os == :macosx - attach_function :_NSGetEnviron, [], :pointer - def self.environ - _NSGetEnviron().read_pointer - end - elsif respond_to? :attach_variable - attach_variable :environ, :pointer - end - - attach_function :strerror, [:int], :string - attach_function :chdir, [:string], :int - attach_function :fcntl, [:int, :int, :int], :int # fcntl actually takes varags, but we only need this version. - - # int posix_spawnp( - # pid_t *restrict pid, - # const char *restrict file, - # const posix_spawn_file_actions_t *file_actions, - # const posix_spawnattr_t *restrict attrp, - # char *const argv[restrict], - # char *const envp[restrict] - # ); - - attach_function :posix_spawnp, [ - :pointer, - :string, - :pointer, - :pointer, - :pointer, - :pointer - ], :int - - # int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions); - attach_function :posix_spawn_file_actions_init, [:pointer], :int - - # int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions); - attach_function :posix_spawn_file_actions_destroy, [:pointer], :int - - # int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, int filedes); - attach_function :posix_spawn_file_actions_addclose, [:pointer, :int], :int - - # int posix_spawn_file_actions_addopen( - # posix_spawn_file_actions_t *restrict file_actions, - # int filedes, - # const char *restrict path, - # int oflag, - # mode_t mode - # ); - attach_function :posix_spawn_file_actions_addopen, [:pointer, :int, :string, :int, :mode_t], :int - - # int posix_spawn_file_actions_adddup2( - # posix_spawn_file_actions_t *file_actions, - # int filedes, - # int newfiledes - # ); - attach_function :posix_spawn_file_actions_adddup2, [:pointer, :int, :int], :int - - # int posix_spawnattr_init(posix_spawnattr_t *attr); - attach_function :posix_spawnattr_init, [:pointer], :int - - # int posix_spawnattr_destroy(posix_spawnattr_t *attr); - attach_function :posix_spawnattr_destroy, [:pointer], :int - - # int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags); - attach_function :posix_spawnattr_setflags, [:pointer, :short], :int - - # int posix_spawnattr_getflags(const posix_spawnattr_t *restrict attr, short *restrict flags); - attach_function :posix_spawnattr_getflags, [:pointer, :pointer], :int - - # int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup); - attach_function :posix_spawnattr_setpgroup, [:pointer, :pid_t], :int - - # int posix_spawnattr_getpgroup(const posix_spawnattr_t *restrict attr, pid_t *restrict pgroup); - attach_function :posix_spawnattr_getpgroup, [:pointer, :pointer], :int - - # int posix_spawnattr_setsigdefault(posix_spawnattr_t *restrict attr, const sigset_t *restrict sigdefault); - attach_function :posix_spawnattr_setsigdefault, [:pointer, :pointer], :int - - # int posix_spawnattr_getsigdefault(const posix_spawnattr_t *restrict attr, sigset_t *restrict sigdefault); - attach_function :posix_spawnattr_getsigdefault, [:pointer, :pointer], :int - - # int posix_spawnattr_setsigmask(posix_spawnattr_t *restrict attr, const sigset_t *restrict sigmask); - attach_function :posix_spawnattr_setsigmask, [:pointer, :pointer], :int - - # int posix_spawnattr_getsigmask(const posix_spawnattr_t *restrict attr, sigset_t *restrict sigmask); - attach_function :posix_spawnattr_getsigmask, [:pointer, :pointer], :int - - def self.check(errno) - if errno != 0 - raise Error, Lib.strerror(FFI.errno) - end - end - - class FileActions - def initialize - @ptr = FFI::MemoryPointer.new(1, Platform::SIZEOF.fetch(:posix_spawn_file_actions_t), false) - Lib.check Lib.posix_spawn_file_actions_init(@ptr) - end - - def add_close(fileno) - Lib.check Lib.posix_spawn_file_actions_addclose( - @ptr, - fileno - ) - end - - def add_open(fileno, path, oflag, mode) - Lib.check Lib.posix_spawn_file_actions_addopen( - @ptr, - fileno, - path, - oflag, - mode - ) - end - - def add_dup(fileno, new_fileno) - Lib.check Lib.posix_spawn_file_actions_adddup2( - @ptr, - fileno, - new_fileno - ) - end - - def free - Lib.check Lib.posix_spawn_file_actions_destroy(@ptr) - @ptr = nil - end - - def to_ptr - @ptr - end - end # FileActions - - class Attrs - def initialize - @ptr = FFI::MemoryPointer.new(1, Platform::SIZEOF.fetch(:posix_spawnattr_t), false) - Lib.check Lib.posix_spawnattr_init(@ptr) - end - - def free - Lib.check Lib.posix_spawnattr_destroy(@ptr) - @ptr = nil - end - - def flags=(flags) - Lib.check Lib.posix_spawnattr_setflags(@ptr, flags) - end - - def flags - ptr = FFI::MemoryPointer.new(:short) - Lib.check Lib.posix_spawnattr_getflags(@ptr, ptr) - - ptr.read_short - end - - def pgroup=(pid) - self.flags |= Platform::POSIX_SPAWN_SETPGROUP - Lib.check Lib.posix_spawnattr_setpgroup(@ptr, pid) - end - - def to_ptr - @ptr - end - end # Attrs - - end - end -end - -# missing on rubinius -class FFI::MemoryPointer - unless method_defined?(:from_string) - def self.from_string(str) - ptr = new(1, str.bytesize + 1) - ptr.write_string("#{str}\0") - - ptr - end - end -end diff --git a/lib/childprocess/unix/platform/i386-linux.rb b/lib/childprocess/unix/platform/i386-linux.rb deleted file mode 100644 index ecf86ed..0000000 --- a/lib/childprocess/unix/platform/i386-linux.rb +++ /dev/null @@ -1,12 +0,0 @@ -module ChildProcess::Unix::Platform - SIZEOF = { - :posix_spawn_file_actions_t => 76, - :posix_spawnattr_t => 336, - :sigset_t => 128 - } - POSIX_SPAWN_RESETIDS = 1 - POSIX_SPAWN_SETPGROUP = 2 - POSIX_SPAWN_SETSIGDEF = 4 - POSIX_SPAWN_SETSIGMASK = 8 - POSIX_SPAWN_USEVFORK = 64 -end diff --git a/lib/childprocess/unix/platform/i386-solaris.rb b/lib/childprocess/unix/platform/i386-solaris.rb deleted file mode 100644 index 5b55788..0000000 --- a/lib/childprocess/unix/platform/i386-solaris.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ChildProcess::Unix::Platform - SIZEOF = { - :posix_spawn_file_actions_t => 4, - :posix_spawnattr_t => 4, - :sigset_t => 16 - } - POSIX_SPAWN_RESETIDS = 1 - POSIX_SPAWN_SETPGROUP = 2 - POSIX_SPAWN_SETSIGDEF = 4 - POSIX_SPAWN_SETSIGMASK = 8 -end diff --git a/lib/childprocess/unix/platform/x86_64-linux.rb b/lib/childprocess/unix/platform/x86_64-linux.rb deleted file mode 100644 index b0c8777..0000000 --- a/lib/childprocess/unix/platform/x86_64-linux.rb +++ /dev/null @@ -1,12 +0,0 @@ -module ChildProcess::Unix::Platform - SIZEOF = { - :posix_spawn_file_actions_t => 80, - :posix_spawnattr_t => 336, - :sigset_t => 128 - } - POSIX_SPAWN_RESETIDS = 1 - POSIX_SPAWN_SETPGROUP = 2 - POSIX_SPAWN_SETSIGDEF = 4 - POSIX_SPAWN_SETSIGMASK = 8 - POSIX_SPAWN_USEVFORK = 64 -end diff --git a/lib/childprocess/unix/platform/x86_64-macosx.rb b/lib/childprocess/unix/platform/x86_64-macosx.rb deleted file mode 100644 index fc0383d..0000000 --- a/lib/childprocess/unix/platform/x86_64-macosx.rb +++ /dev/null @@ -1,11 +0,0 @@ -module ChildProcess::Unix::Platform - SIZEOF = { - :posix_spawn_file_actions_t => 8, - :posix_spawnattr_t => 8, - :sigset_t => 4 - } - POSIX_SPAWN_RESETIDS = 1 - POSIX_SPAWN_SETPGROUP = 2 - POSIX_SPAWN_SETSIGDEF = 4 - POSIX_SPAWN_SETSIGMASK = 8 -end diff --git a/lib/childprocess/unix/posix_spawn_process.rb b/lib/childprocess/unix/posix_spawn_process.rb deleted file mode 100644 index 9622eeb..0000000 --- a/lib/childprocess/unix/posix_spawn_process.rb +++ /dev/null @@ -1,134 +0,0 @@ -require 'ffi' -require 'thread' - -module ChildProcess - module Unix - class PosixSpawnProcess < Process - private - - @@cwd_lock = Mutex.new - - def launch_process - pid_ptr = FFI::MemoryPointer.new(:pid_t) - actions = Lib::FileActions.new - attrs = Lib::Attrs.new - - if io.stdout - actions.add_dup fileno_for(io.stdout), fileno_for(STDOUT) - else - actions.add_open fileno_for(STDOUT), "/dev/null", File::WRONLY, 0644 - end - - if io.stderr - actions.add_dup fileno_for(io.stderr), fileno_for(STDERR) - else - actions.add_open fileno_for(STDERR), "/dev/null", File::WRONLY, 0644 - end - - if duplex? - reader, writer = ::IO.pipe - actions.add_dup fileno_for(reader), fileno_for(STDIN) - actions.add_close fileno_for(writer) - end - - attrs.pgroup = 0 if leader? - attrs.flags |= Platform::POSIX_SPAWN_USEVFORK if defined? Platform::POSIX_SPAWN_USEVFORK - - # wrap in helper classes in order to avoid GC'ed pointers - argv = Argv.new(@args) - envp = Envp.new(ENV.to_hash.merge(@environment)) - - ret = 0 - @@cwd_lock.synchronize do - Dir.chdir(@cwd || Dir.pwd) do - if ChildProcess.jruby? - # on JRuby, the current working directory is for some reason not inherited. - # We'll work around it by making a chdir call through FFI. - # TODO: report this to JRuby - Lib.chdir Dir.pwd - end - - ret = Lib.posix_spawnp( - pid_ptr, - @args.first, # TODO: not sure this matches exec() behaviour - actions, - attrs, - argv, - envp - ) - end - end - - if duplex? - io._stdin = writer - reader.close - end - - actions.free - attrs.free - - if ret != 0 - raise LaunchError, "#{Lib.strerror(ret)} (#{ret})" - end - - @pid = pid_ptr.read_int - ::Process.detach(@pid) if detach? - end - - if ChildProcess.jruby? - def fileno_for(obj) - ChildProcess::JRuby.posix_fileno_for(obj) - end - else - def fileno_for(obj) - obj.fileno - end - end - - class Argv - def initialize(args) - @ptrs = args.map do |e| - if e.include?("\0") - raise ArgumentError, "argument cannot contain null bytes: #{e.inspect}" - end - - FFI::MemoryPointer.from_string(e.to_s) - end - - @ptrs << FFI::Pointer.new(0) - end - - def to_ptr - argv = FFI::MemoryPointer.new(:pointer, @ptrs.size) - argv.put_array_of_pointer(0, @ptrs) - - argv - end - end # Argv - - class Envp - def initialize(env) - @ptrs = env.map do |key, val| - next if val.nil? - - if key =~ /=|\0/ || val.to_s.include?("\0") - raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.to_s.inspect}" - end - - FFI::MemoryPointer.from_string("#{key}=#{val.to_s}") - end.compact - - @ptrs << FFI::Pointer.new(0) - end - - def to_ptr - env = FFI::MemoryPointer.new(:pointer, @ptrs.size) - env.put_array_of_pointer(0, @ptrs) - - env - end - end # Envp - - end - end -end diff --git a/lib/childprocess/unix/process_spawn_process.rb b/lib/childprocess/unix/process_spawn_process.rb index a7e97f1..0d7026e 100644 --- a/lib/childprocess/unix/process_spawn_process.rb +++ b/lib/childprocess/unix/process_spawn_process.rb @@ -1,3 +1,6 @@ +require_relative 'io' +require_relative 'process' + module ChildProcess module Unix class PosixSpawnProcess < Process From f481c2cc19e7d242e14df85275305cddfbde0a0c Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 1 Mar 2021 00:57:56 +0100 Subject: [PATCH 4/5] Add CI workflow --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3312cd4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,17 @@ +name: CI +on: [push, pull_request] +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ ubuntu, macos, windows ] + ruby: [ 2.6.6 ] + runs-on: ${{ matrix.os }}-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: bundle exec rake From 0eff297e3af25449c847716f7b0ac0c7aaebbc49 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 1 Mar 2021 01:00:10 +0100 Subject: [PATCH 5/5] try --- lib/childprocess.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/childprocess.rb b/lib/childprocess.rb index 5f3e0f9..f0b4e9e 100644 --- a/lib/childprocess.rb +++ b/lib/childprocess.rb @@ -19,7 +19,8 @@ def new(*args) when :macosx, :linux, :solaris, :bsd, :cygwin, :aix Unix::PosixSpawnProcess.new(args) when :windows - Windows::Process.new(args) + Unix::PosixSpawnProcess.new(args) + # Windows::Process.new(args) else raise Error, "unsupported platform #{platform_name.inspect}" end