core/process_mirror.rb
module Rubinius
class Mirror
module Process
def self.set_status_global(status)
::Thread.current[:$?] = status
end
def self.fork
Rubinius.primitive :vm_fork
raise PrimitiveFailure, "Rubinius::Mirror::Process.fork primitive failed"
end
def self.exec(*args)
exe = Execute.new(*args)
exe.spawn_setup
exe.exec exe.command, exe.argv
end
def self.spawn(*args)
exe = Execute.new(*args)
begin
pid = exe.spawn exe.options, exe.command, exe.argv
rescue SystemCallError => error
set_status_global ::Process::Status.new(pid, 127)
raise error
end
pid
end
def self.backtick(str)
Rubinius.primitive :vm_backtick
raise PrimitiveFailure, "Rubinius::Mirror::Process.backtick primitive failed"
end
class Execute
attr_reader :command
attr_reader :argv
attr_reader :options
# Turns the various varargs incantations supported by Process.spawn into a
# [env, prog, argv, redirects, options] tuple.
#
# The following method signature is supported:
# Process.spawn([env], command, ..., [options])
#
# The env and options hashes are optional. The command may be a variable
# number of strings or an Array full of strings that make up the new process's
# argv.
#
# Assigns @environment, @command, @argv, @redirects, @options. All
# elements are guaranteed to be non-nil. When no env or options are
# given, empty hashes are returned.
def initialize(env_or_cmd, *args)
if options = Rubinius::Type.try_convert(args.last, ::Hash, :to_hash)
args.pop
end
if env = Rubinius::Type.try_convert(env_or_cmd, ::Hash, :to_hash)
unless command = args.shift
raise ArgumentError, "command argument expected"
end
else
command = env_or_cmd
end
if args.empty? and
cmd = Rubinius::Type.try_convert(command, ::String, :to_str)
raise Errno::ENOENT if cmd.empty?
@command = cmd
@argv = []
else
if cmd = Rubinius::Type.try_convert(command, ::Array, :to_ary)
raise ArgumentError, "wrong first argument" unless cmd.size == 2
command = StringValue(cmd[0])
name = StringValue(cmd[1])
else
name = command = StringValue(command)
end
argv = [name]
args.each { |arg| argv << StringValue(arg) }
@command = command
@argv = argv
end
@options = Rubinius::LookupTable.new if options or env
if options
options.each do |key, value|
case key
when ::IO, ::Fixnum, :in, :out, :err
from = convert_io_fd key
to = convert_to_fd value, from
redirect @options, from, to
when ::Array
from = convert_io_fd key.first
to = convert_to_fd value, from
key.each { |k| redirect @options, convert_io_fd(k), to }
when :unsetenv_others
if value
array = @options[:env] = []
ENV.each_key { |k| array << convert_env_key(k) << nil }
end
when :pgroup
if value == true
value = 0
elsif value
value = Rubinius::Type.coerce_to value, ::Integer, :to_int
raise ArgumentError, "negative process group ID : #{value}" if value < 0
end
@options[key] = value
when :chdir
@options[key] = Rubinius::Type.coerce_to_path(value)
when :umask
@options[key] = value
when :close_others
@options[key] = value if value
else
raise ArgumentError, "unknown exec option: #{key.inspect}"
end
end
end
if env
array = (@options[:env] ||= [])
env.each do |key, value|
array << convert_env_key(key) << convert_env_value(value)
end
end
end
private :initialize
def redirect(options, from, to)
case to
when ::Fixnum
map = (options[:redirect_fd] ||= [])
map << from << to
when ::Array
map = (options[:assign_fd] ||= [])
map << from
map.concat to
end
end
def convert_io_fd(obj)
case obj
when ::Fixnum
obj
when :in
0
when :out
1
when :err
2
when ::IO
obj.fileno
else
raise ArgementError, "wrong exec option: #{obj.inspect}"
end
end
def convert_to_fd(obj, target)
case obj
when ::Fixnum
obj
when :in
0
when :out
1
when :err
2
when :close
nil
when ::IO
obj.fileno
when ::String
[obj, default_mode(target), 0644]
when ::Array
case obj.size
when 1
[obj[0], File::RDONLY, 0644]
when 2
if obj[0] == :child
convert_to_fd obj[1], target
else
[obj[0], convert_file_mode(obj[1]), 0644]
end
when 3
[obj[0], convert_file_mode(obj[1]), obj[2]]
end
else
raise ArgumentError, "wrong exec redirect: #{obj.inspect}"
end
end
def default_mode(target)
if target == 1 or target == 2
OFLAGS["w"]
else
OFLAGS["r"]
end
end
def convert_file_mode(obj)
case obj
when ::Fixnum
obj
when ::String
OFLAGS[obj]
when nil
OFLAG["r"]
else
Rubinius::Type.coerce_to obj, Integer, :to_int
end
end
def convert_env_key(key)
key = Rubinius::Type.check_null_safe(StringValue(key))
if key.include?("=")
raise ArgumentError, "environment name contains a equal : #{key}"
end
key
end
def convert_env_value(value)
return if value.nil?
Rubinius::Type.check_null_safe(StringValue(value))
end
def spawn_setup
Rubinius.invoke_primitive :vm_spawn_setup, @options
end
def spawn(exe, command, args)
Rubinius.primitive :vm_spawn
raise PrimitiveFailure,
"Rubinius::Mirror::Process::Execute#spawn primitive failed"
end
def exec(command, args)
Rubinius.primitive :vm_exec
raise PrimitiveFailure,
"Rubinius::Mirror::Process::Execute#exec primitive failed"
end
end
end
end
end