aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/compiler.rb
blob: 2d819e5c8156c91a4ed2d33080f174d101b46b8c (plain)
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
require "shellwords"

class Compiler < ApplicationRecord
  def to_s
    "#{language} #{version}"
  end

  def run_lazy(snippet)
    r = Result.prepare_execution(self, snippet)
    ExecuteJob.perform_later(self, snippet, r)
    r
  end

  def run(snippet)
    r = Result.prepare_execution(self, snippet)
    run_body(snippet, r)
    r
  end

  private
  def run_body(snippet, r)
    baseroot = Rails.root.join("playground/base").to_s
    env_overlay = Rails.root.join("playground").join(language).join(version).to_s
    sf = Tempfile.open
    sf.write(snippet.code)
    sf.fsync
    of = Tempfile.open
    ef = Tempfile.open
    pid = spawn(Rails.root.join("sandbox/safe_runner").to_s, baseroot, env_overlay, sf.path, *Shellwords.split(command_line),
                in: :close, # TODO
                out: of,
                err: ef)
    _, pst = Process.waitpid2(pid)
    ef.rewind
    err = ef.read.force_encoding(Encoding::BINARY)
    if pst.signaled? || pst.exitstatus > 0
      result = :errored
      status = -1
      Logger.error(err)
      err = nil
    else
      rx, status = err.slice!(0, 8).unpack("ii")
      result = rx == 0 ? :success : :failed
    end

    of.rewind
    output = of.read(65535)
    r.update!(output: output,
              truncated: !of.eof?,
              status: status,
              result: result,
              error: err)
  rescue => e
    r.update!(status: -1, result: :errored)
    raise e
  ensure
    sf.close if sf
    of.close if of
  end
end