163 lines
4.9 KiB

use super::JobType;
use crate::{models::schema::*, UError, UResult};
use diesel::{Identifiable, Insertable, Queryable};
use guess_host_triple::guess_host_triple;
use serde::{Deserialize, Serialize};
use std::fmt;
use uuid::Uuid;
#[derive(Serialize, Deserialize, Clone, Debug, Queryable, Identifiable, Insertable)]
#[table_name = "jobs"]
pub struct JobMeta {
pub alias: Option<String>,
/// string like `bash -c {} -a 1 --arg2`,
/// where {} is replaced by executable's tmp path
pub argv: String,
pub id: Uuid,
pub exec_type: JobType,
//pub schedule: JobSchedule,
pub platform: String,
pub payload: Option<Vec<u8>>,
}
impl JobMeta {
pub fn builder() -> JobMetaBuilder {
JobMetaBuilder::default()
}
pub fn from_shell<S: Into<String>>(cmd: S) -> UResult<Self> {
Self::builder().with_shell(cmd).build()
}
}
impl fmt::Display for JobMeta {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut out = format!("Job: {}", self.id);
if self.alias.is_some() {
out += &format!(" ({})", self.alias.as_ref().unwrap());
}
out += &format!("\nArgv: {}", self.argv);
out += &format!("\nExecutable type: {}", self.exec_type);
out += &format!("\nPlatform: {}", self.platform);
if self.exec_type == JobType::Shell && self.payload.is_some() {
let payload = self.payload.as_ref().unwrap();
let (pld_len, large) = {
let pl = payload.len();
if pl > 20 {
(20, true)
} else {
(pl, false)
}
};
let pld_beginning = payload
.iter()
.take(pld_len)
.map(|u| *u)
.collect::<Vec<u8>>();
out += &format!(
"\nPayload: {}{}",
String::from_utf8_lossy(&pld_beginning),
if large { "" } else { " <...>" }
);
}
write!(f, "{}", out)
}
}
impl Default for JobMeta {
fn default() -> Self {
Self {
id: Uuid::new_v4(),
alias: None,
argv: String::new(),
exec_type: JobType::Shell,
platform: guess_host_triple().unwrap_or("unknown").to_string(),
payload: None,
}
}
}
pub struct JobMetaBuilder {
inner: JobMeta,
}
impl Default for JobMetaBuilder {
fn default() -> Self {
Self {
inner: JobMeta::default(),
}
}
}
impl JobMetaBuilder {
pub fn with_shell<S: Into<String>>(mut self, shell_cmd: S) -> Self {
self.inner.argv = shell_cmd.into();
self
}
pub fn with_payload<C: Into<Vec<u8>>>(mut self, payload: C) -> Self {
self.inner.payload = Some(payload.into());
self
}
pub fn with_alias<S: Into<String>>(mut self, alias: S) -> Self {
self.inner.alias = Some(alias.into());
self
}
pub fn with_type(mut self, e_type: JobType) -> Self {
self.inner.exec_type = e_type;
self
}
pub fn build(self) -> UResult<JobMeta> {
let mut inner = self.inner;
match inner.exec_type {
JobType::Shell => {
if inner.argv == "" {
inner.argv = String::from("/bin/bash -c {}")
}
let argv_parts =
shlex::split(&inner.argv).ok_or(UError::JobArgsError("Shlex failed".into()))?;
let empty_err = UError::JobArgsError("Empty argv".into());
if argv_parts.get(0).ok_or(empty_err.clone())?.len() == 0 {
return Err(empty_err);
}
match inner.payload.as_ref() {
Some(_) => {
if !inner.argv.contains("{}") {
return Err(UError::JobArgsError(
"Argv contains no executable placeholder".into(),
));
} else {
()
}
}
None => {
if inner.argv.contains("{}") {
return Err(UError::JobArgsError(
"No payload provided, but argv contains executable placeholder"
.into(),
));
} else {
()
}
}
};
Ok(inner)
}
JobType::Manage => Ok(inner),
_ => todo!(),
}
}
/*
pub fn from_file(path: PathBuf) -> UResult<Self> {
let data = fs::read(path)
.map_err(|e| UError::FilesystemError(
path.to_string_lossy().to_string(),
e.to_string()
))?;
let filename = path.file_name().unwrap().to_str().unwrap();
}*/
}