Skip to content

Commit

Permalink
Prepare runguard
Browse files Browse the repository at this point in the history
  • Loading branch information
slhmy authored Oct 26, 2024
1 parent 262f0c8 commit b98a477
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
members = ["judge-core", "judger"]
resolver = "2"
members = ["judge-core", "judger", "runguard"]
resolver = "2"
11 changes: 11 additions & 0 deletions runguard/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "runguard"
version = "0.1.0"
edition = "2021"
build = "build.rs"

[dependencies]
libc = "0.2"

clap = { version = "4", features = ["derive"] }
humantime = "2"
5 changes: 5 additions & 0 deletions runguard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# runguard

A Rust version of
[Domjudge runguard](https://github.com/DOMjudge/domjudge/blob/main/judge/runguard.cc)
written in C++.
3 changes: 3 additions & 0 deletions runguard/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("cargo:rustc-link-lib=cgroup");
}
60 changes: 60 additions & 0 deletions runguard/src/cgroup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use std::ffi::CString;
use std::fs::File;
use std::io::{self, BufRead};
use std::os::raw::c_char;

struct CGroup {
cgroup: *mut libc::c_void,
}

impl CGroup {
fn new(name: &str) -> Self {
let cgroup_name = CString::new(name).expect("CString::new failed");
unsafe {
let cgroup = cgroup_new_cgroup(cgroup_name.as_ptr());
if cgroup.is_null() {
eprintln!("Failed to create new cgroup");
} else {
println!("Successfully created new cgroup {}", name);
}
CGroup { cgroup }
}
}
}

extern "C" {
fn cgroup_new_cgroup(name: *const c_char) -> *mut libc::c_void;
}

fn cgroup_is_v2() -> bool {
let file = match File::open("/proc/mounts") {
Ok(file) => file,
Err(_) => {
eprintln!("Error opening /proc/mounts");
return false;
}
};

let reader = io::BufReader::new(file);
for line in reader.lines() {
if let Ok(line) = line {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3 && parts[1] == "/sys/fs/cgroup" && parts[2] == "cgroup2" {
return true;
}
}
}

false
}

#[test]
fn test_cgroup() {
let _ = CGroup::new("my_cgroup");

if cgroup_is_v2() {
println!("cgroup v2 is enabled");
} else {
println!("cgroup v2 is not enabled");
}
}
102 changes: 102 additions & 0 deletions runguard/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::path;

use clap::Parser;

#[derive(Parser)]
#[command(
override_usage = "runguard [OPTION]... <COMMAND>...",
about = "Run COMMAND with specified options.",
after_help = "Note that root privileges are needed for the `root' and `user' options. \
If `user' is set, then `group' defaults to the same to prevent security issues, \
since otherwise the process would retain group root permissions. \
The COMMAND path is relative to the changed ROOT directory if specified. \
TIME may be specified as a float; two floats separated by `:' are treated as soft and hard limits. \
The runtime written to file is that of the last of wall/cpu time options set, \
and defaults to CPU time when neither is set. \
When run setuid without the `user' option, the user ID is set to the real user ID."
)]
pub struct Cli {
/// run COMMAND with root directory set to ROOT
#[arg(short, long)]
root: String,

/// run COMMAND as user with username or ID USER
#[arg(short, long)]
user: String,

/// run COMMAND under group with name or ID GROUP
#[arg(short, long)]
group: String,

/// change to directory DIR after setting root directory
#[arg(short = 'd', long, value_name = "DIR")]
chdir: String,

/// kill COMMAND after TIME wallclock seconds
#[arg(short = 't', long, value_name = "TIME")]
walltime: humantime::Duration,

/// set maximum CPU time to TIME seconds
#[arg(short = 'C', long, value_name = "TIME")]
cputime: humantime::Duration,

/// set total memory limit to SIZE kB
#[arg(short = 'm', long, value_name = "SIZE")]
memsize: u64,

/// set maximum created filesize to SIZE kB
#[arg(short = 'f', long, value_name = "SIZE")]
filesize: u64,

/// set maximum no. processes to N
#[arg(short = 'p', long, value_name = "N")]
nproc: u64,

/// use only processor number ID (or set, e.g. \"0,2-3\")
#[arg(short = 'P', long, value_name = "ID")]
cpuset: String,

/// disable core dumps
#[arg(short = 'c', long)]
no_core: bool,

/// redirect COMMAND stdout output to FILE
#[arg(short = 'o', long, value_name = "FILE")]
stdout: path::PathBuf,

/// redirect COMMAND stderr output to FILE
#[arg(short = 'e', long, value_name = "FILE")]
stderr: path::PathBuf,

/// truncate COMMAND stdout/stderr streams at SIZE kB
#[arg(short, long, value_name = "SIZE")]
streamsize: u64,

/// preserve environment variables (default only PATH)
#[arg(short = 'E', long)]
environment: String,

/// write metadata (runtime, exitcode, etc.) to FILE
#[arg(short = 'M', long, value_name = "FILE")]
metadata: path::PathBuf,

/// process ID of runpipe to send SIGUSR1 signal when
/// timelimit is reached
#[arg(short = 'U', long, value_name = "PID")]
runpipepid: u32,

/// display some extra warnings and information
#[arg(short, long)]
verbose: bool,

/// suppress all warnings and verbose output
#[arg(short, long)]
quiet: bool,

/// output version information and exit
#[arg(long)]
version: bool,

#[arg(required = true)]
command: Vec<String>,
}
8 changes: 8 additions & 0 deletions runguard/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use clap::Parser;

mod cgroup;
mod cli;

fn main() {
let _ = cli::Cli::parse();
}

0 comments on commit b98a477

Please sign in to comment.