Skip to content

Commit

Permalink
Prepare runguard
Browse files Browse the repository at this point in the history
  • Loading branch information
slhmy committed Nov 22, 2024
1 parent 262f0c8 commit ae60437
Show file tree
Hide file tree
Showing 13 changed files with 584 additions and 4 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/rust-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ jobs:
- name: Check format
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
# run: cargo clippy --all-targets --all-features -- -D warnings
run: cargo clippy --all-targets --all-features
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y libseccomp-dev protobuf-compiler
run: |
sudo apt-get update
sudo apt-get install -y libseccomp-dev protobuf-compiler libcgroup-dev
- name: Check ENV
run: echo $(rustup --version && g++ -v)
- name: Build test dist
Expand Down
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"
1 change: 1 addition & 0 deletions runguard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
metafile.txt
13 changes: 13 additions & 0 deletions runguard/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "runguard"
version = "0.1.0"
edition = "2021"
build = "build.rs"

[dependencies]
libc = "0.2"
nix = { version = "0.29", features = ["signal"] }

clap = { version = "4", features = ["derive"] }
chrono = "0.4"
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");
}
129 changes: 129 additions & 0 deletions runguard/src/cgroup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use std::ffi::CString;
use std::fs::File;
use std::io::{self, BufRead};
use std::os::raw::c_char;

use crate::context::Context;

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

pub enum CGroupError {
ECGROUPNOTCOMPILED = 50000,
ECGROUPNOTMOUNTED,
ECGROUPNOTEXIST,
ECGROUPNOTCREATED,
ECGROUPSUBSYSNOTMOUNTED,
ECGROUPNOTOWNER,
/** Controllers bound to different mount points */
ECGROUPMULTIMOUNTED,
/* This is the stock error. Default error. @todo really? */
ECGROUPNOTALLOWED,
ECGMAXVALUESEXCEEDED,
ECGCONTROLLEREXISTS,
ECGVALUEEXISTS,
ECGINVAL,
ECGCONTROLLERCREATEFAILED,
ECGFAIL,
ECGROUPNOTINITIALIZED,
ECGROUPVALUENOTEXIST,
/**
* Represents error coming from other libraries like glibc. @c libcgroup
* users need to check cgroup_get_last_errno() upon encountering this
* error.
*/
ECGOTHER,
ECGROUPNOTEQUAL,
ECGCONTROLLERNOTEQUAL,
/** Failed to parse rules configuration file. */
ECGROUPPARSEFAIL,
/** Rules list does not exist. */
ECGROUPNORULES,
ECGMOUNTFAIL,
/**
* Not an real error, it just indicates that iterator has come to end
* of sequence and no more items are left.
*/
ECGEOF = 50023,
/** Failed to parse config file (cgconfig.conf). */
ECGCONFIGPARSEFAIL,
ECGNAMESPACEPATHS,
ECGNAMESPACECONTROLLER,
ECGMOUNTNAMESPACE,
ECGROUPUNSUPP,
ECGCANTSETVALUE,
/** Removing of a group failed because it was not empty. */
ECGNONEMPTY,
}

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

impl CGroup {
fn new(mut ctx: Context, 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() {
ctx.error(0, format_args!("cgroup_new_cgroup"));
} else {
ctx.verbose(format_args!("cgroup_new_cgroup: {}", name));
}
CGroup { ctx, cgroup }
}
}
}

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
}

pub fn cgroup_strerror_safe(err: i32) -> String {
unsafe {
let errstr = cgroup_strerror(err);
let errstr = std::ffi::CStr::from_ptr(errstr).to_str().unwrap();
errstr.to_string()
}
}

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

if cgroup_is_v2() {
println!("cgroup v2 is enabled");
} else {
println!("cgroup v2 is not enabled");
}
}

#[test]
fn test_cgroup_strerror() {
println!(
"{}",
cgroup_strerror_safe(CGroupError::ECGROUPNOTCOMPILED as i32)
);
}
104 changes: 104 additions & 0 deletions runguard/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::path;

use clap::Parser;

use crate::types::SoftHardTime;

#[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)]
pub root: String,

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

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

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

// For `TIME` values, the format is `soft:hard`.
/// kill COMMAND after TIME wallclock seconds
#[arg(short = 't', long, value_name = "TIME")]
pub walltime: SoftHardTime,

/// set maximum CPU time to TIME seconds
#[arg(short = 'C', long, value_name = "TIME")]
pub cputime: SoftHardTime,
// /// set total memory limit to SIZE kB
// #[arg(short = 'm', long, value_name = "SIZE")]
// pub memsize: u64,

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

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

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

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

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

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

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

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

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

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

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

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

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

// #[arg(required = true)]
// pub command: Vec<String>,
}
Loading

0 comments on commit ae60437

Please sign in to comment.