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