module Process def self.time Rubinius.primitive :vm_time raise PrimitiveFailure, "Process.time primitive failed" end def self.cpu_times Rubinius.primitive :vm_times raise PrimitiveFailure, "Process.cpu_times primitive failed" end ## # Sets the process title. Calling this method does not affect the value of # `$0` as per MRI behaviour. This method returns the title set. # # @param [String] title # @return [Title] # def self.setproctitle(title) val = Rubinius::Type.coerce_to(title, String, :to_str) Rubinius.invoke_primitive(:vm_set_process_title, val) end def self.setrlimit(resource, cur_limit, max_limit=undefined) resource = coerce_rlimit_resource(resource) cur_limit = Rubinius::Type.coerce_to cur_limit, Integer, :to_int unless undefined.equal? max_limit max_limit = Rubinius::Type.coerce_to max_limit, Integer, :to_int end rlimit = Rlimit.new rlimit[:rlim_cur] = cur_limit rlimit[:rlim_max] = undefined.equal?(max_limit) ? cur_limit : max_limit ret = FFI::Platform::POSIX.setrlimit(resource, rlimit.pointer) Errno.handle if ret == -1 nil end def self.getrlimit(resource) resource = coerce_rlimit_resource(resource) lim_max = [] rlimit = Rlimit.new ret = FFI::Platform::POSIX.getrlimit(resource, rlimit.pointer) Errno.handle if ret == -1 [rlimit[:rlim_cur], rlimit[:rlim_max]] end def self.setsid pgid = FFI::Platform::POSIX.setsid Errno.handle if pgid == -1 pgid end def self.fork pid = Rubinius::Mirror::Process.fork if block_given? and pid.nil? begin yield nil status = 0 rescue SystemExit => e status = e.status rescue Exception => e e.render "An exception occurred in a forked block" status = 1 end until Rubinius::AtExit.empty? begin Rubinius::AtExit.shift.call rescue SystemExit => e status = e.status end end # Do not use Kernel.exit. This raises a SystemExit exception, which # will run ensure blocks. This is not what MRI does and causes bugs # in programs. See issue http://github.com/rubinius/rubinius/issues#issue/289 for # an example Kernel.exit! status end pid end def self.times Struct::Tms.new(*cpu_times) end def self.kill(signal, *pids) raise ArgumentError, "PID argument required" if pids.length == 0 use_process_group = false signal = signal.to_s if signal.kind_of?(Symbol) if signal.kind_of?(String) if signal[0] == ?- signal = signal[1..-1] use_process_group = true end if signal[0..2] == "SIG" signal = signal[3..-1] end signal = Signal::Names[signal] end raise ArgumentError unless signal.kind_of? Fixnum if signal < 0 signal = -signal use_process_group = true end pids.each do |pid| pid = Rubinius::Type.coerce_to_pid pid pid = -pid if use_process_group result = FFI::Platform::POSIX.kill(pid, signal) Errno.handle if result == -1 end return pids.length end def self.abort(msg=nil) if msg msg = StringValue(msg) $stderr.puts(msg) end raise SystemExit.new(1, msg) end def self.getpgid(pid) pid = Rubinius::Type.coerce_to pid, Integer, :to_int ret = FFI::Platform::POSIX.getpgid(pid) Errno.handle if ret == -1 ret end def self.setpgid(pid, int) pid = Rubinius::Type.coerce_to pid, Integer, :to_int int = Rubinius::Type.coerce_to int, Integer, :to_int ret = FFI::Platform::POSIX.setpgid(pid, int) Errno.handle if ret == -1 ret end @maxgroups = 32 class << self attr_reader :maxgroups def maxgroups=(m) @maxgroups = m end end def self.setpgrp setpgid(0, 0) end def self.getpgrp ret = FFI::Platform::POSIX.getpgrp Errno.handle if ret == -1 ret end def self.pid ret = FFI::Platform::POSIX.getpid Errno.handle if ret == -1 ret end def self.ppid ret = FFI::Platform::POSIX.getppid Errno.handle if ret == -1 ret end def self.uid=(uid) # the 4 rescue clauses below are needed # until respond_to? can be used to query the implementation of methods attached via FFI # atm respond_to returns true if a method is attached but not implemented on the platform uid = Rubinius::Type.coerce_to uid, Integer, :to_int begin ret = FFI::Platform::POSIX.setresuid(uid, -1, -1) rescue NotImplementedError begin ret = FFI::Platform::POSIX.setreuid(uid, -1) rescue NotImplementedError begin ret = FFI::Platform::POSIX.setruid(uid) rescue NotImplementedError if Process.euid == uid ret = FFI::Platform::POSIX.setuid(uid) else raise NotImplementedError end end end end Errno.handle if ret == -1 uid end def self.gid=(gid) gid = Rubinius::Type.coerce_to gid, Integer, :to_int Process::Sys.setgid gid end def self.euid=(uid) # the 4 rescue clauses below are needed # until respond_to? can be used to query the implementation of methods attached via FFI # atm respond_to returns true if a method is attached but not implemented on the platform uid = Rubinius::Type.coerce_to uid, Integer, :to_int begin ret = FFI::Platform::POSIX.setresuid(-1, uid, -1) rescue NotImplementedError begin ret = FFI::Platform::POSIX.setreuid(-1, uid) rescue NotImplementedError begin ret = FFI::Platform::POSIX.seteuid(uid) rescue NotImplementedError if Process.uid == uid ret = FFI::Platform::POSIX.setuid(uid) else raise NotImplementedError end end end end Errno.handle if ret == -1 uid end def self.egid=(gid) gid = Rubinius::Type.coerce_to gid, Integer, :to_int Process::Sys.setegid gid end def self.uid ret = FFI::Platform::POSIX.getuid Errno.handle if ret == -1 ret end def self.gid ret = FFI::Platform::POSIX.getgid Errno.handle if ret == -1 ret end def self.euid ret = FFI::Platform::POSIX.geteuid Errno.handle if ret == -1 ret end def self.egid ret = FFI::Platform::POSIX.getegid Errno.handle if ret == -1 ret end def self.getpriority(kind, id) kind = Rubinius::Type.coerce_to kind, Integer, :to_int id = Rubinius::Type.coerce_to id, Integer, :to_int FFI::Platform::POSIX.errno = 0 ret = FFI::Platform::POSIX.getpriority(kind, id) Errno.handle ret end def self.setpriority(kind, id, priority) kind = Rubinius::Type.coerce_to kind, Integer, :to_int id = Rubinius::Type.coerce_to id, Integer, :to_int priority = Rubinius::Type.coerce_to priority, Integer, :to_int ret = FFI::Platform::POSIX.setpriority(kind, id, priority) Errno.handle if ret == -1 ret end def self.groups g = [] count = Rubinius::FFI::Platform::POSIX.getgroups(0, nil) FFI::MemoryPointer.new(:int, count) { |p| num_groups = FFI::Platform::POSIX.getgroups(count, p) Errno.handle if num_groups == -1 g = p.read_array_of_int(num_groups) } g end def self.groups=(g) @maxgroups = g.length if g.length > @maxgroups FFI::MemoryPointer.new(:int, @maxgroups) { |p| p.write_array_of_int(g) Errno.handle if FFI::Platform::POSIX.setgroups(g.length, p) == -1 } g end def self.initgroups(username, gid) username = StringValue(username) gid = Rubinius::Type.coerce_to gid, Integer, :to_int if FFI::Platform::POSIX.initgroups(username, gid) == -1 Errno.handle end Process.groups end # # Wait for the given process to exit. # # The pid may be the specific pid of some previously forked # process, or -1 to indicate to watch for *any* child process # exiting. Other options, such as process groups, may be available # depending on the system. # # With no arguments the default is to block waiting for any # child processes (pid -1.) # # The flag may be Process::WNOHANG, which indicates that # the child should only be quickly checked. If it has not # exited yet, nil is returned immediately instead. # # The return value is the exited pid or nil if Process::WNOHANG # was used and the child had not yet exited. # # If the pid has exited, the global $? is set to a Process::Status # object representing the exit status (and possibly other info) of # the child. # # If there exists no such pid (e.g. never forked or already # waited for), or no children at all, Errno::ECHILD is raised. # # TODO: Support other options such as WUNTRACED? --rue # def self.wait2(input_pid=-1, flags=nil) input_pid = Rubinius::Type.coerce_to input_pid, Integer, :to_int if flags and (flags & WNOHANG) == WNOHANG value = Rubinius.invoke_primitive :vm_waitpid, input_pid, true return if value.nil? else value = Rubinius.invoke_primitive :vm_waitpid, input_pid, false end if value == false raise Errno::ECHILD, "No child process: #{input_pid}" end # vm_waitpid returns a tuple when wait needs to communicate the pid that # was actually detected as stopped (since wait can wait for all child # pids, groups, etc) status, termsig, stopsig, pid = value status = Process::Status.new(pid, status, termsig, stopsig) Rubinius::Mirror::Process.set_status_global status [pid, status] end # # Wait for all child processes. # # Blocks until all child processes have exited, and returns # an Array of [pid, Process::Status] results, one for each # child. # # Be mindful of the effects of creating new processes while # .waitall has been called (usually in a different thread.) # The .waitall call does not in any way check that it is only # waiting for children that existed at the time it was called. # def self.waitall statuses = [] begin while true statuses << Process.wait2 end rescue Errno::ECHILD end statuses end def self.wait(pid=-1, flags=nil) pid, status = Process.wait2(pid, flags) return pid end class << self alias_method :waitpid, :wait alias_method :waitpid2, :wait2 end def self.daemon(stay_in_dir=false, keep_stdio_open=false) # Do not run at_exit handlers in the parent exit!(0) if fork Process.setsid exit!(0) if fork Dir.chdir("/") unless stay_in_dir unless keep_stdio_open io = File.open "/dev/null", File::RDWR, 0 $stdin.reopen io $stdout.reopen io $stderr.reopen io end return 0 end def self.exec(*args) Rubinius::Mirror::Process.exec(*args) end def self.spawn(*args) Rubinius::Mirror::Process.spawn(*args) end # TODO: Should an error be raised on ECHILD? --rue # # TODO: This operates on the assumption that waiting on # the event consumes very little resources. If this # is not the case, the check should be made WNOHANG # and called periodically. # def self.detach(pid) raise ArgumentError, "Only positive pids may be detached" unless pid > 0 thread = Thread.new { Process.wait pid; $? } thread[:pid] = pid def thread.pid; self[:pid] end thread end def self.coerce_rlimit_resource(resource) case resource when Integer return resource when Symbol, String # do nothing else unless r = Rubinius::Type.check_convert_type(resource, String, :to_str) return Rubinius::Type.coerce_to resource, Integer, :to_int end resource = r end constant = "RLIMIT_#{resource}" unless const_defined? constant raise ArgumentError, "invalid resource name: #{constant}" end const_get constant end class << self private :coerce_rlimit_resource end #-- # TODO: Most of the fields aren't implemented yet. # TODO: Also, these objects should only need to be constructed by # Process.wait and family. #++ class Status attr_reader :termsig attr_reader :stopsig def initialize(pid=nil, status=nil, termsig=nil, stopsig=nil) @pid = pid @status = status @termsig = termsig @stopsig = stopsig end private :initialize def exitstatus @status end def to_i @status end def to_s @status.to_s end def &(num) @status & num end def ==(other) other = other.to_i if other.kind_of? Process::Status @status == other end def >>(num) @status >> num end def coredump? false end def exited? @status != nil end def pid @pid end def signaled? @termsig != nil end def stopped? @stopsig != nil end def success? if exited? @status == 0 else nil end end end module Sys class << self def getegid ret = FFI::Platform::POSIX.getegid Errno.handle if ret == -1 ret end def geteuid ret = FFI::Platform::POSIX.geteuid Errno.handle if ret == -1 ret end def getgid ret = FFI::Platform::POSIX.getgid Errno.handle if ret == -1 ret end def getuid ret = FFI::Platform::POSIX.getuid Errno.handle if ret == -1 ret end def issetugid raise "not implemented" end def setgid(gid) gid = Rubinius::Type.coerce_to gid, Integer, :to_int ret = FFI::Platform::POSIX.setgid gid Errno.handle if ret == -1 nil end def setuid(uid) uid = Rubinius::Type.coerce_to uid, Integer, :to_int ret = FFI::Platform::POSIX.setuid uid Errno.handle if ret == -1 nil end def setegid(egid) egid = Rubinius::Type.coerce_to egid, Integer, :to_int ret = FFI::Platform::POSIX.setegid egid Errno.handle if ret == -1 nil end def seteuid(euid) euid = Rubinius::Type.coerce_to euid, Integer, :to_int ret = FFI::Platform::POSIX.seteuid euid Errno.handle if ret == -1 nil end def setrgid(rgid) setregid(rgid, -1) end def setruid(ruid) setreuid(ruid, -1) end def setregid(rid, eid) rid = Rubinius::Type.coerce_to rid, Integer, :to_int eid = Rubinius::Type.coerce_to eid, Integer, :to_int ret = FFI::Platform::POSIX.setregid rid, eid Errno.handle if ret == -1 nil end def setreuid(rid, eid) rid = Rubinius::Type.coerce_to rid, Integer, :to_int eid = Rubinius::Type.coerce_to eid, Integer, :to_int ret = FFI::Platform::POSIX.setreuid rid, eid Errno.handle if ret == -1 nil end def setresgid(rid, eid, sid) rid = Rubinius::Type.coerce_to rid, Integer, :to_int eid = Rubinius::Type.coerce_to eid, Integer, :to_int sid = Rubinius::Type.coerce_to sid, Integer, :to_int ret = FFI::Platform::POSIX.setresgid rid, eid, sid Errno.handle if ret == -1 nil end def setresuid(rid, eid, sid) rid = Rubinius::Type.coerce_to rid, Integer, :to_int eid = Rubinius::Type.coerce_to eid, Integer, :to_int sid = Rubinius::Type.coerce_to sid, Integer, :to_int ret = FFI::Platform::POSIX.setresuid rid, eid, sid Errno.handle if ret == -1 nil end end end module UID class << self def change_privilege(uid) uid = Rubinius::Type.coerce_to uid, Integer, :to_int ret = FFI::Platform::POSIX.setreuid(uid, uid) Errno.handle if ret == -1 uid end def eid ret = FFI::Platform::POSIX.geteuid Errno.handle if ret == -1 ret end def eid=(uid) uid = Rubinius::Type.coerce_to uid, Integer, :to_int ret = FFI::Platform::POSIX.seteuid(uid) Errno.handle if ret == -1 uid end alias_method :grant_privilege, :eid= def re_exchange real = FFI::Platform::POSIX.getuid Errno.handle if real == -1 eff = FFI::Platform::POSIX.geteuid Errno.handle if eff == -1 ret = FFI::Platform::POSIX.setreuid(eff, real) Errno.handle if ret == -1 eff end def re_exchangeable? true end def rid ret = FFI::Platform::POSIX.getuid Errno.handle if ret == -1 ret end def sid_available? true end def switch eff = re_exchange if block_given? ret = yield re_exchange return ret else return eff end end end end module GID class << self def change_privilege(gid) gid = Rubinius::Type.coerce_to gid, Integer, :to_int ret = FFI::Platform::POSIX.setregid(gid, gid) Errno.handle if ret == -1 gid end def eid ret = FFI::Platform::POSIX.getegid Errno.handle if ret == -1 ret end def eid=(gid) gid = Rubinius::Type.coerce_to gid, Integer, :to_int ret = FFI::Platform::POSIX.setegid(gid) Errno.handle if ret == -1 gid end alias_method :grant_privilege, :eid= def re_exchange real = FFI::Platform::POSIX.getgid Errno.handle if real == -1 eff = FFI::Platform::POSIX.getegid Errno.handle if eff == -1 ret = FFI::Platform::POSIX.setregid(eff, real) Errno.handle if ret == -1 eff end def re_exchangeable? true end def rid ret = FFI::Platform::POSIX.getgid Errno.handle if ret == -1 ret end def sid_available? true end def switch eff = re_exchange if block_given? ret = yield re_exchange return ret else return eff end end end end end