use crate::config::Job;
use std::io;
use log::*;
use libc::{kill, pid_t};
use std::fs;
use std::fs::OpenOptions;
pub fn match_builtin(job: &Job) -> bool {
return match job.command().as_str() {
"kill" => {
true
}
"reboot" => {
true
}
"truncate" => {
true
}
"copy" => {
true
}
_ => {
false
}
};
}
pub fn execute_builtin(job: &Job) -> bool {
return match job.command().as_str() {
"kill" => {
execute_kill(job)
}
"reboot" => {
execute_reboot(job)
}
"truncate" => {
execute_truncate(job)
}
"copy" => {
execute_copy(job)
}
_ => {
false
}
};
}
pub fn execute_kill(job: &Job) -> bool {
if job.args().len() == 2 {
let signal = job.args().get(0).unwrap();
if let Ok(mut sig_int) = signal.parse::<pid_t>() {
if sig_int < 0 {
sig_int = sig_int * -1;
}
let pid_file = job.args().get(1).unwrap();
if let Ok(pid_string) = fs::read_to_string(pid_file) {
if let Ok(pid_int) = pid_string.trim().parse::<i32>() {
let result = unsafe {
kill(pid_int, sig_int)
};
if result == 0 {
return true;
}
}
} else {
warn!("builtin kill pid file error: {}", job.args().join(" "));
return false;
}
}
}
warn!("builtin kill error: {}", job.args().join(" "));
false
}
pub fn execute_reboot(_job: &Job) -> bool {
unsafe {
kill(1, 15);
};
true
}
fn truncate_file(path: &String) -> io::Result<()> {
OpenOptions::new()
.write(true)
.truncate(true)
.open(path)?;
Ok(())
}
pub fn execute_truncate(job: &Job) -> bool {
if job.args() .len() == 0 {
return false;
}
let mut count = job.args() .len();
for file_path in job.args() {
if let Ok(_) = truncate_file(file_path) {
count = count - 1;
}
}
warn!("builtin truncate error: {}", job.args().join(" "));
count == 0
}
pub fn execute_copy(job: &Job) -> bool {
if job.has_file_spec() && job.args().len() == 1 && ! job.file_spec().is_dir() {
let file_path = job.file_spec().path();
let dest_file = job.args().get(0).unwrap();
return if let Ok(_) = fs::copy(file_path, dest_file) {
true
} else {
warn!("builtin copy error: {}", job.args().join(" "));
false
}
}
warn!("builtin copy syntax error: {}", job.args().join(" "));
false
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::VERBOSE;
use crate::config::FileSpec;
use std::path::Path;
use std::sync::atomic::Ordering;
#[test]
pub fn test_builtins_happy_paths() {
VERBOSE.store(true, Ordering::Relaxed);
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("kill -15 999999"));
assert!(match_builtin(&job));
assert!(! execute_kill(&job)); assert!(! execute_builtin(&job));
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("reboot"));
assert!(match_builtin(&job));
let file_spec = FileSpec::new("./tests/test-truncate.txt".to_string());
let job = Job::new(None, Some(file_spec), std::env::var("USER").unwrap().as_str(), String::from("copy ./target/to-truncate.txt"));
assert!(match_builtin(&job));
assert!(execute_copy(&job)); assert!(execute_builtin(&job));
assert!(Path::new("./tests/test-truncate.txt").exists());
assert!(Path::new("./target/to-truncate.txt").exists());
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("truncate ./target/to-truncate.txt"));
assert!(match_builtin(&job));
assert!(execute_truncate(&job)); assert!(execute_builtin(&job));
assert!(Path::new("./target/to-truncate.txt").exists());
if let Ok(meta) = fs::metadata("./target/to-truncate.txt") {
assert_eq!(meta.len(), 0);
let _ = fs::remove_file("./target/to-truncate.txt");
}
}
#[test]
pub fn test_builtins_unhappy_paths() {
VERBOSE.store(true, Ordering::Relaxed);
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("/bin/updatedb"));
assert!(! match_builtin(&job));
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("kill -15"));
assert!(! execute_kill(&job)); let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("kill"));
assert!(! execute_kill(&job));
let file_spec = Some(FileSpec::new("./tests/test-truncate.txt".to_string()));
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("copy ./tests/to-truncate.txt"));
assert!(! execute_copy(&job)); let job = Job::new(None, file_spec, std::env::var("USER").unwrap().as_str(), String::from("copy"));
assert!(! execute_copy(&job));
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("truncate"));
assert!(! execute_truncate(&job));
}
}