Temporary tag for this failure. Updated CI spec coming.
[rbx.git] / kernel / core / process.rb
blob9412a86090f0a2154a1824f6f649e5ff9901e0f0
1 # depends on: module.rb class.rb hash.rb struct.rb
3 module Process
4   module Constants
5     WNOHANG = 1
6     WUNTRACED = 2
7     PRIO_PROCESS  = Rubinius::RUBY_CONFIG['rbx.platform.process.PRIO_PROCESS']
8     PRIO_PGRP  = Rubinius::RUBY_CONFIG['rbx.platform.process.PRIO_PGRP']
9     PRIO_USER  = Rubinius::RUBY_CONFIG['rbx.platform.process.PRIO_USER']
10     RLIMIT_CPU  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_CPU']
11     RLIMIT_FSIZE  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_FSIZE']
12     RLIMIT_DATA  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_DATA']
13     RLIMIT_STACK  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_STACK']
14     RLIMIT_CORE  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_CORE']
15     RLIMIT_RSS  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_RSS']
16     RLIMIT_NPROC  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_NPROC']
17     RLIMIT_NOFILE  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_NOFILE']
18     RLIMIT_MEMLOCK  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_MEMLOCK']
19     RLIMIT_AS  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_AS']
20     RLIM_INFINITY  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIM_INFINITY']
21     RLIM_SAVED_MAX  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIM_SAVED_MAX']
22     RLIM_SAVED_CUR  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIM_SAVED_CUR']
23     RLIMIT_SBSIZE  = Rubinius::RUBY_CONFIG['rbx.platform.process.RLIMIT_SBSIZE']
24   end
25   include Constants
27   class Rlimit < FFI::Struct
28     config "rbx.platform.rlimit", :rlim_cur, :rlim_max
29   end
31   def self.setrlimit(resource, cur_limit, max_limit=Undefined)
32     rlimit = Rlimit.new
33     rlimit[:rlim_cur] = cur_limit
34     rlimit[:rlim_max] = max_limit.equal?(Undefined) ? cur_limit : max_limit
35     Errno.handle if -1 == Platform::POSIX.setrlimit(resource, rlimit.pointer)
36     nil
37   end
39   def self.getrlimit(resource)
40     lim_max = []
41     rlimit = Rlimit.new
42     Errno.handle if -1 == Platform::POSIX.getrlimit(resource, rlimit.pointer)
43     lim_max = [rlimit[:rlim_cur], rlimit[:rlim_max]]
44     lim_max
45   end
47   def self.setsid
48     pgid = Platform::POSIX.setsid
49     Errno.handle if -1 == pgid
50     pgid
51   end
53   def self.fork
54     pid = fork_prim
55     pid = nil if pid == 0
56     if block_given? and pid.nil?
57       yield nil
58       Kernel.exit
59     end
60     pid
61   end
63   def self.sleep(sec)
64     micro_sleep(sec * 1_000_000)
65   end
66   
67   def self.usleep(sec)
68     micro_sleep(sec * 1_000)
69   end
70   
71   def self.times
72     now = Time.now
73     Struct::Tms.new(now - $STARTUP_TIME, 0.0, 0.0, 0.0)
74   end
76   def self.kill(sig, pid)
77     use_process_group = false
78     if sig.kind_of?(String)
79       if sig[0] == 45
80         sig = sig[1..-1]
81         use_process_group = true
82       end
83       if sig[0..2] == "SIG"
84         sig = sig[3..-1]
85       end
86       number = Signal::Names[sig]
87     else
88       number = sig.to_i
89       if number < 0
90         number = -number
91         use_process_group = true
92       end
93     end
94     pid = -pid if use_process_group
95     raise ArgumentError unless number
96     ret = Platform::POSIX.kill(pid, number)
97     case ret
98     when 0
99       return 1
100     when -1
101       Errno.handle
102     end
103   end
105   def self.abort(msg=nil)
106     $stderr.puts(msg) if(msg)
107     exit 1
108   end
109   def self.exit!(code=0)
110     exit(code)
111   end
113   def self.getpgid(pid)
114     ret = Platform::POSIX.getpgid(pid)
115     Errno.handle if ret == -1
116     ret
117   end
119   def self.setpgid(pid, int)
120     ret = Platform::POSIX.setpgid(pid, int)
121     Errno.handle if ret == -1
122     ret
123   end
125   @maxgroups = 32
126   class << self
127     attr_reader :maxgroups
128     def maxgroups=(m)
129       @maxgroups = m
130     end
131   end
133   def self.setpgrp
134     setpgid(0, 0)
135   end
136   def self.getpgrp
137     ret = Platform::POSIX.getpgrp
138     Errno.handle if ret == -1
139     ret
140   end
142   def self.pid
143     ret = Platform::POSIX.getpid
144     Errno.handle if ret == -1
145     ret
146   end
148   def self.ppid
149     ret = Platform::POSIX.getppid
150     Errno.handle if ret == -1
151     ret
152   end
154   def self.uid=(uid)
155     Process::Sys.setuid uid
156   end
158   def self.gid=(gid)
159     Process::Sys.setgid gid
160   end
162   def self.euid=(uid)
163     Process::Sys.seteuid uid
164   end
166   def self.egid=(gid)
167     Process::Sys.setegid gid
168   end
170   def self.uid
171     ret = Platform::POSIX.getuid
172     Errno.handle if ret == -1
173     ret
174   end
176   def self.gid
177     ret = Platform::POSIX.getgid
178     Errno.handle if ret == -1
179     ret
180   end
182   def self.euid
183     ret = Platform::POSIX.geteuid
184     Errno.handle if ret == -1
185     ret
186   end
188   def self.egid
189     ret = Platform::POSIX.getegid
190     Errno.handle if ret == -1
191     ret
192   end
194   def self.getpriority(kind, id)
195     Platform::POSIX.errno = 0
196     ret = Platform::POSIX.getpriority(kind, id)
197     Errno.handle
198     ret
199   end
201   def self.setpriority(kind, id, priority)
202     ret = Platform::POSIX.setpriority(kind, id, priority)
203     Errno.handle if ret == -1
204     ret
205   end
207   def self.groups
208     g = []
209     MemoryPointer.new(:int, @maxgroups) { |p|
210       num_groups = Platform::POSIX.getgroups(@maxgroups, p)
211       Errno.handle if num_groups == -1
212       g = p.read_array_of_int(num_groups)
213     }
214     g
215   end
217   def self.groups=(g)
218     @maxgroups = g.length if g.length > @maxgroups
219     MemoryPointer.new(:int, @maxgroups) { |p|
220       p.write_array_of_int(g)
221       Errno.handle if -1 == Platform::POSIX.setgroups(g.length, p)
222     }
223     g
224   end
226   def self.initgroups(username, gid)
227     Errno.handle if -1 == Platform::POSIX.initgroups(username, gid)
228     Process.groups
229   end
231   def self.wait(pid=-1, flags=0)
232     chan = Channel.new
233     Scheduler.send_on_stopped(chan, pid, flags)
234     pid, status = chan.receive
235     case pid
236     when false
237       raise Errno::ECHILD
238     when nil
239       return nil
240     else
241       $? = Process::Status.new pid, status
242     end
243     return pid
244   end
246   def self.waitall
247     statuses = []
248     statuses << [Process.wait, $?] while true
249   rescue Errno::ECHILD
250     return statuses
251   end
253   def self.wait2(pid=-1, flags=0)
254     pid = Process.wait(pid, flags)
255     pid ? [pid, $?] : nil
256   end
258   class << self
259     alias_method :waitpid, :wait
260     alias_method :waitpid2, :wait2
261   end
263   def self.detach(pid)
264     thr = Thread.new {
265       while true
266         break if Process.wait(pid, Process::WNOHANG)
267         sleep(1)
268       end
269     }
270   end
272   #--
273   # TODO: Most of the fields aren't implemented yet.
274   # TODO: Also, these objects should only need to be constructed by
275   # Process.wait and family.
276   #++
278   class Status
279     def initialize(pid, status)
280       @pid = pid
281       @status = status
282     end
283     
284     def to_i
285       @status
286     end
287     
288     def to_s
289       @status.to_s
290     end
291     
292     def &(num)
293       @status & num
294     end
295     
296     def ==(other)
297       other = other.to_i if other.kind_of? Process::Status
298       @status == other
299     end
300     
301     def >>(num)
302       @status >> num
303     end
304     
305     def coredump?
306       false
307     end
308     
309     def exited?
310       true
311     end
312     
313     def exitstatus
314       @status
315     end
316     
317     def pid
318       @pid
319     end
320     
321     def signaled?
322       false
323     end
324     
325     def stopped?
326       false
327     end
328     
329     def stopsig
330       nil
331     end
332     
333     def success?
334       @status == 0
335     end
336     
337     def termsig
338       nil
339     end
340   end
342   module Sys
343     class << self
344       def getegid
345         ret = Platform::POSIX.getegid
346         Errno.handle if ret == -1
347         ret
348       end
349       def geteuid
350         ret = Platform::POSIX.geteuid
351         Errno.handle if ret == -1
352         ret
353       end
354       def getgid
355         ret = Platform::POSIX.getgid
356         Errno.handle if ret == -1
357         ret
358       end
359       def getuid
360         ret = Platform::POSIX.getuid
361         Errno.handle if ret == -1
362         ret
363       end
364       def issetugid
365         raise "not implemented"
366       end
367       def setgid(gid)
368         Platform::POSIX.setgid gid
369         Errno.handle if ret == -1
370         nil
371       end
372       def setuid(uid)
373         Platform::POSIX.setuid uid
374         Errno.handle if ret == -1
375         nil
376       end
377       def setegid(egid)
378         ret = Platform::POSIX.setegid egid
379         Errno.handle if ret == -1
380         nil
381       end
382       def seteuid(euid)
383         Platform::POSIX.seteuid euid
384         Errno.handle if ret == -1
385         nil
386       end
387       def setrgid(rgid)
388         setregid(rgid, -1)
389       end
390       def setruid(ruid)
391         setreuid(ruid, -1)
392       end
393       def setregid(rid, eid)
394         Platform::POSIX.setregid rid, eid
395         Errno.handle if ret == -1
396         nil
397       end
398       def setreuid(rid)
399         Platform::POSIX.setreuid rid
400         Errno.handle if ret == -1
401         nil
402       end
403       def setresgid(rid, eid, sid)
404         Platform::POSIX.setresgid rid, eid, sid
405         Errno.handle if ret == -1
406         nil
407       end
408       def setresuid(rid, eig, sid)
409         Platform::POSIX.setresuid rid, eid, sid
410         Errno.handle if ret == -1
411         nil
412       end
413     end
414   end
416   module UID
417     class << self
418       def change_privilege(uid)
419         Platform::POSIX.setreuid(uid, uid)
420         uid
421       end
423       def eid
424         ret = Platform::POSIX.geteuid
425         Errno.handle if ret == -1
426         ret
427       end
429       def eid=(uid)
430         ret = Platform::POSIX.seteuid(uid)
431         Errno.handle if ret == -1
432         uid
433       end
434       alias_method :grant_privilege, :eid=
436       def re_exchange
437         real = Platform::POSIX.getuid
438         Errno.handle if real == -1
439         eff = Platform::POSIX.geteuid
440         Errno.handle if eff == -1
441         ret = Platform::POSIX.setreuid(eff, real)
442         Errno.handle if ret == -1
443         eff
444       end
446       def re_exchangeable?
447         true
448       end
450       def rid
451         ret = Platform::POSIX.getuid
452         Errno.handle if ret == -1
453         ret
454       end
456       def sid_available?
457         true
458       end
460       def switch
461         eff = re_exchange
462         if block_given?
463           ret = yield
464           re_exchange
465           return ret
466         else
467           return eff
468         end
469       end
471     end
472   end
474   module GID
475     class << self
476       def change_privilege(gid)
477         ret = Platform::POSIX.setregid(gid, gid)
478         Errno.handle if ret == -1
479         gid
480       end
482       def eid
483         ret = Platform::POSIX.getegid
484         Errno.handle if ret == -1
485         ret
486       end
488       def eid=(gid)
489         ret = Platform::POSIX.setegid(gid)
490         Errno.handle if ret == -1
491         gid
492       end
493       alias_method :grant_privilege, :eid=
495       def re_exchange
496         real = Platform::POSIX.getgid
497         Errno.handle if real == -1
498         eff = Platform::POSIX.getegid
499         Errno.handle if eff == -1
500         ret = Platform::POSIX.setregid(eff, real)
501         Errno.handle if ret == -1
502         eff
503       end
505       def re_exchangeable?
506         true
507       end
509       def rid
510         ret = Platform::POSIX.getgid
511         Errno.handle if ret == -1
512         ret
513       end
515       def sid_available?
516         true
517       end
519       def switch
520         eff = re_exchange
521         if block_given?
522           ret = yield
523           re_exchange
524           return ret
525         else
526           return eff
527         end
528       end
530     end
531   end
535 module Kernel
537   def fork(&block)
538     Process.fork(&block)
539   end
540   module_function :fork
542   def system(prog, *args)
543     pid = Process.fork
544     if pid
545       Process.waitpid(pid)
546       $?.exitstatus == 0
547     else
548       exec(prog, *args) rescue exit! 1
549     end
550   end
551   module_function :system
553   def exec(cmd, *args)
554     if args.empty? and cmd.kind_of? String
555       raise SystemCallError if cmd.empty?
556       if /([*?{}\[\]<>()~&|$;'`"\n\s]|[^\w])/.match(cmd)
557         Process.replace "/bin/sh", ["sh", "-c", cmd]
558       else
559         Process.replace cmd, [cmd]
560       end
561     else
562       if cmd.kind_of? Array
563         prog = cmd[0]
564         name = cmd[1]
565       else
566         name = prog = cmd
567       end
569       argv = [name]
570       args.each do |arg|
571         argv << arg.to_s
572       end
574       Process.replace prog, argv
575     end
576   end
577   module_function :exec
579   def `(str) #`
580     str = StringValue(str)
581     read, write = IO.pipe
582     pid = Process.fork
583     if pid
584       write.close
585       chan = Channel.new
586       output = ""
587       buf = String.new(50)
588       while true
589         Scheduler.send_on_readable chan, read, buf, 50
590         res = chan.receive
591         if res.nil?
592           Process.waitpid(pid)
593           return output
594         else
595           output << buf
596         end
597       end
598     else
599       read.close
600       STDOUT.reopen write
601       Process.replace "/bin/sh", ["sh", "-c", str]
602     end
603   end
605   module_function :` # `
608 class Struct
609   def self.after_loaded
610     Struct.new 'Tms', :utime, :stime, :cutime, :cstime
611   end