use crate::config::Job;
use crate::config::VERBOSE;
use std::process::{Command, Stdio};
use std::os::unix::process::CommandExt;
use std::thread;
use std::ffi::{CStr, CString};
use std::sync::atomic::Ordering;
use log::*;
use crate::builtins::{match_builtin, execute_builtin};
pub fn execute_job(job: &Job) -> bool {
if match_builtin(job) {
return execute_builtin(job);
}
let mut command = Command::new(job.command());
for arg in job.args() {
command.arg(arg);
}
command.stdin(Stdio::null());
command.stdout(Stdio::null());
command.stderr(Stdio::null());
String::from("");
command = set_env_vars(command, job);
unsafe {
let mut pwd = std::mem::MaybeUninit::<libc::passwd>::uninit();
let mut result: *mut libc::passwd = 0 as *mut libc::passwd;
let mut buf_len = libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX);
if buf_len == -1 {
buf_len = 16384;
}
let mut buf: Box<[i8]> = vec![0 as i8; buf_len as usize].into_boxed_slice();
libc::getpwnam_r(CString::from_vec_unchecked(job.user().clone().into_bytes()).as_ptr(),
pwd.as_mut_ptr(),
buf.as_mut_ptr(), buf_len as usize,
&mut result);
if std::ptr::null() != result {
command.uid(pwd.assume_init().pw_uid);
command.gid(pwd.assume_init().pw_gid);
let cd_string = CStr::from_ptr(pwd.assume_init().pw_dir).to_str().unwrap().to_owned();
command.current_dir(&cd_string);
command.env("HOME", cd_string);
} else {
if VERBOSE.load(Ordering::Relaxed) {
println!("user not found {}", job.user());
}
warn!("user not found {}", job.user());
return false;
}
}
let child = command.spawn();
if let Err(err) = child {
if VERBOSE.load(Ordering::Relaxed) {
println!("error spawning: '{}' {} {}", err, job.command(), job.args().join(" "));
}
warn!("error spawning: '{}' {} {}", err, job.command(), job.args().join(" "));
false
} else {
if VERBOSE.load(Ordering::Relaxed) {
println!("spawned: {} {}", job.command(), job.args().join(" "));
}
debug!("spawned: {} {}", job.command(), job.args().join(" "));
thread::spawn(|| child.unwrap().wait() );
true
}
}
fn set_env_vars(mut builder: Command, job: &Job) -> Command {
builder
.env_clear()
.env("USERNAME", job.user())
.env("USER", job.user())
.env("SHELL", "/bin/sh")
.env("HOME", "/");
if let Ok(path) = std::env::var("PATH") {
builder.env("PATH", path);
} else {
builder.env("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
}
builder
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn test_execute_job() {
VERBOSE.store(true, Ordering::Relaxed);
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("/bin/echo hello world"));
assert!(execute_job(&job));
let job = Job::new(None, None, std::env::var("USER").unwrap().as_str(), String::from("non_existing_bin"));
assert!(!execute_job(&job));
let job = Job::new(None, None, "non_existing_user", String::from("/bin/echo hello world"));
assert!(!execute_job(&job));
}
}