aboutsummaryrefslogtreecommitdiffstats
path: root/backend/src/run_result.rs
blob: 005324254131a8ccd7553c42afee6e902782b4ba (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
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
use std::collections::BTreeMap;
use rustc_serialize::json::{self, ToJson, Json};
use std::io::Cursor;
use byteorder::{ReadBytesExt, LittleEndian};
use std::fs;
use error::PoeError;
use std::io::{Read, Write};
use snippet::Snippet;
use compiler::Compiler;
use std::process::Output;

#[derive(RustcDecodable, RustcEncodable)]
struct RunResultMetadata {
    pub exit: i32,
    pub result: i32,
    pub elapsed: i32,
    pub message: String,
    pub truncated: bool,
}

pub fn open_render(snip: &Snippet, comp: &Compiler) -> Json {
    let mut map = BTreeMap::new();
    map.insert("compiler".to_string(), comp.render());

    match fs::File::open(format!("{}/results/{}.json", &snip.basedir(), &comp.id)) {
        Ok(mut file) => {
            let mut encoded = String::new();
            file.read_to_string(&mut encoded).unwrap();
            let meta: RunResultMetadata = json::decode(&encoded).unwrap();
            map.insert("exit".to_string(), meta.exit.to_json());
            map.insert("result".to_string(), meta.result.to_json());
            map.insert("elapsed".to_string(), meta.elapsed.to_json());
            map.insert("message".to_string(), meta.message.to_json());
            map.insert("truncated".to_string(), meta.truncated.to_json());
            map.insert("output".to_string(), read_output_str(&snip, &comp).to_json());
        },
        Err(_) => {
            map.insert("result".to_string(), None::<i32>.to_json());
        }
    }

    Json::Object(map)
}

pub fn read_stdout_raw(snip: &Snippet, comp: &Compiler) -> Vec<u8> {
    let mut out = vec![];
    for (fd, raw) in read_output_raw(&snip, &comp) {
        if fd == 1 {
            out.extend_from_slice(&raw); // TODO: this copies
        }
    }
    out
}

fn read_output_str(snip: &Snippet, comp: &Compiler) -> Vec<(u32, String)> {
    read_output_raw(&snip, &comp).iter().map(|pair| {
        (pair.0, String::from_utf8_lossy(&pair.1).into_owned())
    }).collect()
}

fn read_output_raw(snip: &Snippet, comp: &Compiler) -> Vec<(u32, Vec<u8>)> {
    let f = |out: &mut Vec<(u32, Vec<u8>)>| {
        let mut out_file = stry!(fs::File::open(format!("{}/results/{}.output", &snip.basedir(), &comp.id)));
        while let Ok(fd) = out_file.read_u32::<LittleEndian>() {
            let len = stry!(out_file.read_u32::<LittleEndian>());
            if len > 1024 * 1024 { return Err("broken input?".to_string()); } // TODO
            let mut body = vec![0; len as usize];
            stry!(out_file.read_exact(&mut body));
            out.push((fd, body));
        }
        Ok(())
    };

    let mut out = vec![];
    if let Err(err) = f(&mut out) {
        println!("{}", err);
    }
    out
}

pub fn parse_and_save(snip: &Snippet, comp: &Compiler, output: Output) -> Result<(), PoeError> {
    let output_limit = 65536;

    if output.status.success() {
        if output.stderr.len() < 12 {
            return Err(PoeError::from("failed sandbox (result)"));
        }

        let (metavec, msgvec) = output.stderr.split_at(12);
        let mut rdr = Cursor::new(metavec);
        let reason = rdr.read_i32::<LittleEndian>().unwrap();
        let exit = rdr.read_i32::<LittleEndian>().unwrap();
        let elapsed = rdr.read_i32::<LittleEndian>().unwrap();
        let msg_str = String::from_utf8_lossy(&msgvec);
        let trunc = output.stdout.len() > output_limit;
        let meta = RunResultMetadata { exit: exit, result: reason, message: msg_str.into_owned(), truncated: trunc, elapsed: elapsed };

        let mut meta_file = fs::File::create(format!("{}/results/{}.json", &snip.basedir(), &comp.id))?;
        meta_file.write(json::encode(&meta).unwrap().as_bytes())?;

        let mut out_file = fs::File::create(format!("{}/results/{}.output", &snip.basedir(), &comp.id))?;
        out_file.write(if trunc { &output.stdout[..output_limit] } else { &output.stdout })?;

        Ok(())
    } else {
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout).into_owned());
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr).into_owned());
        Err(PoeError::from("failed sandbox (error)"))
    }
}