summary refs log tree commit diff
path: root/tests/plugins.rs
blob: 94b07672a18f879d55f74bc3d192d47f38dfb4bf (plain) (blame)
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
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![cfg(feature = "plugin")]

extern crate rand;

use rand::{thread_rng, Rng};

use std::ffi::OsString;
use std::fs::remove_file;
use std::io::Write;
use std::env::{current_exe, var_os};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thread::sleep;
use std::time::Duration;

struct RemovePath(PathBuf);
impl Drop for RemovePath {
    fn drop(&mut self) {
        if let Err(e) = remove_file(&self.0) {
            eprintln!("failed to remove path: {:?}", e);
        }
    }
}

fn get_target_path() -> PathBuf {
    current_exe()
        .ok()
        .map(|mut path| {
                 path.pop();
                 if path.ends_with("deps") {
                     path.pop();
                 }
                 path
             })
        .expect("failed to get crosvm binary directory")
}

fn build_plugin(src: &str) -> RemovePath {
    let mut out_bin = PathBuf::from("target");
    let libcrosvm_plugin_dir = get_target_path();
    out_bin.push(thread_rng()
                     .gen_ascii_chars()
                     .take(10)
                     .collect::<String>());
    let mut child = Command::new(var_os("CC").unwrap_or(OsString::from("cc")))
        .args(&["-Icrosvm_plugin", "-pthread", "-o"]) // crosvm.h location and set output path.
        .arg(&out_bin)
        .arg("-L") // Path of shared object to link to.
        .arg(&libcrosvm_plugin_dir)
        .arg("-lcrosvm_plugin")
        .arg("-Wl,-rpath") // Search for shared object in the same path when exec'd.
        .arg(&libcrosvm_plugin_dir)
        .args(&["-Wl,-rpath", "."]) // Also check current directory in case of sandboxing.
        .args(&["-xc", "-"]) // Read source code from piped stdin.
        .stdin(Stdio::piped())
        .spawn()
        .expect("failed to spawn compiler");
    {
        let stdin = child.stdin.as_mut().expect("failed to open stdin");
        stdin
            .write_all(src.as_bytes())
            .expect("failed to write source to stdin");
    }

    let status = child.wait().expect("failed to wait for compiler");
    assert!(status.success(), "failed to build plugin");

    RemovePath(PathBuf::from(out_bin))
}

fn run_plugin(bin_path: &Path, with_sandbox: bool) {
    let mut crosvm_path = get_target_path();
    crosvm_path.push("crosvm");
    let mut cmd = Command::new(crosvm_path);
    cmd.args(&["run",
                "-c",
                "1",
                "--seccomp-policy-dir",
                "tests",
                "--plugin"])
        .arg(bin_path
                 .canonicalize()
                 .expect("failed to canonicalize plugin path"));
    if !with_sandbox {
        cmd.arg("--disable-sandbox");
    }

    let mut child = cmd
        .spawn()
        .expect("failed to spawn crosvm");
    for _ in 0..12 {
        match child.try_wait().expect("failed to wait for crosvm") {
            Some(status) => {
                assert!(status.success());
                return;
            }
            None => sleep(Duration::from_millis(100)),
        }
    }
    child.kill().expect("failed to kill crosvm");
    panic!("crosvm process has timed out");
}

fn test_plugin(src: &str) {
    let bin_path = build_plugin(src);
    // Run with and without the sandbox enabled.
    run_plugin(&bin_path.0, false);
    run_plugin(&bin_path.0, true);
}

#[test]
fn test_adder() {
    test_plugin(include_str!("plugin_adder.c"));
}

#[test]
fn test_dirty_log() {
    test_plugin(include_str!("plugin_dirty_log.c"));
}

#[test]
fn test_ioevent() {
    test_plugin(include_str!("plugin_ioevent.c"));
}

#[test]
fn test_irqfd() {
    test_plugin(include_str!("plugin_irqfd.c"));
}