Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dokku-setup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@
default: "hermesAPITokenUnsecure"

- name: ALLOWED_ORIGIN
prompt: "Enter domain you need CORS (format: https://yourdomain.com)"
prompt: "Enter domain you need CORS"
private: no
default: "https://execute.tyxc.org"

- name: RUST_LOG
prompt: "Enter you preferred log settings (error, warn, info [default], debug, trace, full, off)"
Expand Down
9 changes: 5 additions & 4 deletions src/api/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,12 @@ async fn run_execution(state: Arc<AppState>,
) -> Result<Resp, StatusCode> {

tokio::task::spawn_blocking(move || {
let manager = &state.box_manager;

let isolate_box = manager.acquire();
let run_result = execute_code(&isolate_box, req, auth_token);
manager.release(isolate_box);
let compiler_pool = Arc::clone(&state.compiler_pool);
let executor_pool = Arc::clone(&state.executor_pool);

let run_result = execute_code(&compiler_pool, &executor_pool, req, auth_token);

run_result
}).await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
Expand Down
18 changes: 13 additions & 5 deletions src/core/exe.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use tracing::instrument;
use crate::languages::get_handler;
use crate::core::runner::safe_execute;
use crate::core::workers::IsolateBox;
use crate::core::workers::{BoxManager, EphemeralBox, PersistentBox};
use crate::config::models::{ReqMulti, Resp};
use crate::config::utils::get_lang_config;

#[instrument(level = "debug", skip(passed_token))]
pub fn execute_code(isolate_box: &IsolateBox, req: ReqMulti, passed_token: Option<String>) -> Result<Resp, String>{
pub fn execute_code(compiler_pool: &BoxManager<PersistentBox>,
executor_pool: &BoxManager<EphemeralBox>,
req: ReqMulti,
passed_token: Option<String>) -> Result<Resp, String>{

let lang_config = match get_lang_config(&req.language) {
Ok(config) => config,
Err(e) => return Ok(
Expand Down Expand Up @@ -37,9 +41,11 @@ pub fn execute_code(isolate_box: &IsolateBox, req: ReqMulti, passed_token: Optio
}
}

let executor_box = executor_pool.acquire();

let handler = get_handler(&req.language).map_err(|e| e)?;

let work_dir = &isolate_box.path;
let work_dir = &executor_box.path;

let program = match handler.prepare(work_dir, &req) {
Ok(p) => p,
Expand All @@ -55,7 +61,7 @@ pub fn execute_code(isolate_box: &IsolateBox, req: ReqMulti, passed_token: Optio

if lang_config.compile {
let compile_args = handler.compile_cmd(&program);
let (out, log, code, _) = safe_execute(isolate_box, lang_config, &compile_args)?;
let (out, log, code, _) = safe_execute(&executor_box, lang_config, &compile_args)?;
if code != 0 {
return Ok(Resp {
output: out,
Expand All @@ -69,7 +75,9 @@ pub fn execute_code(isolate_box: &IsolateBox, req: ReqMulti, passed_token: Optio
let run_args = handler.run_cmd(&program);

let (output, std_log, code, time_ms) =
safe_execute(&isolate_box, &lang_config, &run_args)?;
safe_execute(&executor_box, &lang_config, &run_args)?;

executor_pool.release(executor_box);

Ok(Resp{output, std_log, code, time_ms})
}
10 changes: 5 additions & 5 deletions src/core/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ use std::process::Command;
use tracing::instrument;
use tokio::time::Instant;

use crate::core::workers::IsolateBox;
use crate::core::workers::{Sandbox};
use crate::config::models::LangConfig;
use crate::config::constants::IS_DEBUG;

#[instrument(level = "debug")]
pub fn safe_execute(isolate_box: &IsolateBox,
#[instrument(level = "debug", skip(config))]
pub fn safe_execute(sand_box: &impl Sandbox,
config: &LangConfig,
run_args: &[String]
) -> Result<(String, String, i32, u128), String> {
Expand All @@ -17,7 +17,7 @@ pub fn safe_execute(isolate_box: &IsolateBox,
let mut cmd = Command::new("isolate");

// Box config
cmd.arg("--box-id").arg(&isolate_box.id.to_string());
cmd.arg("--box-id").arg(&sand_box.id().to_string());
cmd.arg("--cg");

cmd.args(&config.isolate_args);
Expand All @@ -43,7 +43,7 @@ pub fn safe_execute(isolate_box: &IsolateBox,

// Metafile for job
// TODO: read exit code
let meta_path = format!("/tmp/isolate_{}.meta", isolate_box.id);
let meta_path = format!("/tmp/isolate_{}.meta", sand_box.id());
cmd.arg(format!("--meta={}", &meta_path));

cmd.arg("--run");
Expand Down
105 changes: 82 additions & 23 deletions src/core/workers.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
use std::fmt::Debug;
use std::fs;
use std::io::Result;
use std::ops::Range;
use std::path::PathBuf;
use std::process::Command;

use crossbeam_channel::{bounded, Receiver, Sender};
use tracing::info;

pub trait Sandbox: Debug{
fn id(&self) -> usize;
fn path(&self) -> PathBuf;
fn cleanup(&self) -> Result<()>;
}


#[derive(Debug)]
pub struct IsolateBox {
pub struct EphemeralBox {
pub id: usize,
pub path: PathBuf,
}

pub struct BoxManager {
box_pool: Receiver<IsolateBox>,
sender: Sender<IsolateBox>,
}

impl IsolateBox {
pub fn new(id: usize) -> IsolateBox {
impl EphemeralBox {
pub fn new(id: usize) -> Self {
Self {
id,
path: PathBuf::from(format!("/var/lib/isolate/{}/box", id)),
}
}
pub fn cleanup(&self) -> Result<()>{
}

impl Sandbox for EphemeralBox {
fn id(&self) -> usize {
self.id
}

fn path(&self) -> PathBuf {
self.path.clone()
}

fn cleanup(&self) -> Result<()>{
// Remove all temp files without unmounting.
if self.path.exists() {
for entry in fs::read_dir(&self.path)? {
Expand All @@ -38,34 +53,78 @@ impl IsolateBox {
}
}
// Kill all process for that user.
let target_user = format!("isolate-{}", self.id);
let _ = Command::new("pkill")
.args(["-9", "-u", format!("isolate-{}", self.id.to_string()).as_str()])
.args(["-9", "-u"])
.arg(&target_user)
.output();
Ok(())
}
}

#[derive(Debug)]
pub struct PersistentBox {
pub id: usize,
pub path: PathBuf,
}

impl PersistentBox {
pub fn new(id: usize) -> Self {
Self {
id,
path: PathBuf::from(format!("/var/lib/isolate/{}/box", id)),
}
}
}

impl Sandbox for PersistentBox {
fn id(&self) -> usize {
self.id
}

fn path(&self) -> PathBuf {
self.path.clone()
}

fn cleanup(&self) -> Result<()>{
Ok(())
}
}

#[derive(Debug)]
pub struct BoxManager<T: Sandbox> {
box_pool: Receiver<T>,
sender: Sender<T>,
}



impl<T:Sandbox> BoxManager<T> {
pub fn new<F> (id_range: Range<usize>, init_box: F) -> Self
where
F: Fn(usize) -> T,
{

let count = id_range.len();

impl BoxManager {
pub fn new(count: usize) -> Self {
let (tx, rx) = bounded(count as usize);
let (tx, rx) = bounded(count);

for i in 0..count{
for id in id_range {
// Clean up previous boxes on restart.
let _ = Command::new("isolate")
.args(["--box-id", &i.to_string(), "--cg", "--cleanup"])
.args(["--box-id", &id.to_string(), "--cg", "--cleanup"])
.status();

//TODO: Add warning for control group per kernel version
let status = Command::new("isolate")
.args(["--box-id", &i.to_string(), "--cg", "--init"]).status()
.args(["--box-id", &id.to_string(), "--cg", "--init"]).status()
.expect("Failed to execute command. Verify isolate installation.");

if !status.success() {
panic!("{}", format!("Failed to initialize box {}", i.to_string()));
panic!("{}", format!("Failed to initialize box {}", id.to_string()));
}

let b = IsolateBox::new(i);
let b = init_box(id);
tx.send(b).unwrap();
}

Expand All @@ -77,14 +136,14 @@ impl BoxManager {
}
}

pub fn acquire(&self) -> IsolateBox {
pub fn acquire(&self) -> T {
self.box_pool.recv().expect("Box pool closed.")
}

pub fn release(&self, isolate_box: IsolateBox) {
if let Err(e) = isolate_box.cleanup(){
eprintln!("Cleanup failed for box: {} with: {}", isolate_box.id, e);
pub fn release(&self, target_box: T) {
if let Err(e) = target_box.cleanup(){
eprintln!("Cleanup failed for box: {} with: {}", target_box.id(), e);
}
self.sender.send(isolate_box).unwrap();
self.sender.send(target_box).unwrap();
}
}
13 changes: 10 additions & 3 deletions src/state.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
use std::sync::Arc;

use crate::core::workers::BoxManager;
use crate::core::workers::{BoxManager, EphemeralBox, PersistentBox};

pub struct AppState {
pub box_manager: Arc<BoxManager>,
pub executor_pool: Arc<BoxManager<EphemeralBox>>,
pub compiler_pool: Arc<BoxManager<PersistentBox>>
}

impl AppState {
pub fn new(worker_count: usize) -> Self {

let ten_percent = (worker_count as f64 * 0.1).ceil() as usize;
AppState {
box_manager: Arc::new(BoxManager::new(worker_count))
executor_pool: Arc::new(BoxManager::new(0..worker_count, |id| EphemeralBox::new(id))),
compiler_pool: Arc::new(
BoxManager::new(worker_count..worker_count + ten_percent, |id| PersistentBox::new(id)
)
),
}
}
}