wip: working on cron

This commit is contained in:
2023-11-28 00:08:27 +11:00
parent 6e60f58145
commit 5a0c901281
7 changed files with 289 additions and 6 deletions

View File

@@ -266,6 +266,10 @@ impl Config {
self.services.iter()
}
pub fn get_cron_iter(&self) -> std::slice::Iter<Crontab> {
self.cron.iter()
}
pub fn get_shell(&self) -> Option<String> {
if let Some(shell) = &self.shell_path {
return Some(shell.clone());
@@ -282,3 +286,84 @@ impl Clone for Command {
}
}
}
impl Clone for Crontab {
fn clone(&self) -> Self {
Self {
minute: self.minute.clone(),
hour: self.hour.clone(),
day_of_month: self.day_of_month.clone(),
month: self.month.clone(),
day_of_week: self.day_of_week.clone(),
command: self.command.clone()
}
}
}
impl Clone for CronTimeFieldSpec {
fn clone(&self) -> Self {
match self {
CronTimeFieldSpec::Any => CronTimeFieldSpec::Any,
CronTimeFieldSpec::Every(x) => CronTimeFieldSpec::Every(*x),
CronTimeFieldSpec::Exact(x) => CronTimeFieldSpec::Exact(*x),
CronTimeFieldSpec::MultiOccurrence(x) => {
CronTimeFieldSpec::MultiOccurrence(x.clone())
},
}
}
}
struct CronFieldCmpHelper<'a>(u8, u8, Option<&'a Vec<u8>>);
impl PartialEq for CronTimeFieldSpec {
fn eq(&self, other: &Self) -> bool {
let lhs: CronFieldCmpHelper;
let rhs: CronFieldCmpHelper;
match self {
CronTimeFieldSpec::Any => { lhs = CronFieldCmpHelper(0, 0, None); }
CronTimeFieldSpec::Every(x) => { lhs = CronFieldCmpHelper(1, *x, None); }
CronTimeFieldSpec::Exact(x) => { lhs = CronFieldCmpHelper(2, *x, None); }
CronTimeFieldSpec::MultiOccurrence(v) => { lhs = CronFieldCmpHelper(3, 0, Some(v)); }
}
match other {
CronTimeFieldSpec::Any => { rhs = CronFieldCmpHelper(0, 0, None); }
CronTimeFieldSpec::Every(x) => { rhs = CronFieldCmpHelper(1, *x, None); }
CronTimeFieldSpec::Exact(x) => { rhs = CronFieldCmpHelper(2, *x, None); }
CronTimeFieldSpec::MultiOccurrence(v) => { rhs = CronFieldCmpHelper(3, 0, Some(v)); }
}
if lhs.0 == rhs.0 {
if lhs.0 == 3u8 {
if let Some(lv) = lhs.2 {
if let Some(rv) = rhs.2 {
if lv.len() != rv.len() {
return false;
}
let mut l_iter = lv.iter();
let mut r_iter = rv.iter();
'item: loop {
if let Some(liv) = l_iter.next() {
if let Some(riv) = r_iter.next() {
if *liv != *riv {
return false;
}
} else {
break 'item;
}
} else {
break 'item;
}
}
return true;
}
}
} else {
return lhs.1 == rhs.1;
}
}
false
}
}

View File

@@ -18,15 +18,16 @@ pub async fn start(cfg: config::Config) -> Result<(), WingmateInitError> {
let waiter_cancel_sighandler = sighandler_cancel.clone();
let cancel = CancellationToken::new();
let starter_cancel = cancel.clone();
let starter_service_cancel = cancel.clone();
let starter_cron_cancel = cancel.clone();
let mut set: JoinSet<Result<(), wmerr::WingmateInitError>> = JoinSet::new();
set.spawn(async move {
sighandler::sighandler(sig_sync_flag, cancel, sighandler_cancel).await
});
//TODO: start the process starter
starter::start_services(&mut set, &cfg, starter_cancel)?;
starter::start_services(&mut set, &cfg, starter_service_cancel)?;
starter::start_cron(&mut set, &cfg, starter_cron_cancel)?;
//TODO: spawn_blocking for waiter
set.spawn_blocking(move || {

View File

@@ -1,3 +1,5 @@
mod time_calc;
use tokio::task::JoinSet;
use tokio::process::{Command, Child};
use tokio_util::sync::CancellationToken;
@@ -10,8 +12,9 @@ use nix::sys::signal::{kill, Signal};
use nix::errno::Errno;
use nix::unistd::Pid;
use anyhow::Context;
use crate::init::config;
use crate::init::error::WingmateInitError;
use time::OffsetDateTime;
use crate::init::config::{self, CronTimeFieldSpec};
use crate::init::error::{WingmateInitError, CronConfigError};
pub fn start_services(ts: &mut JoinSet<Result<(), WingmateInitError>>, cfg: &config::Config, cancel: CancellationToken)
@@ -102,7 +105,37 @@ fn result_match(result: tokio_result<ExitStatus>) -> Result<(), anyhow::Error> {
}
}
dbg!("starter: sleep exited");
Ok(())
}
pub fn start_cron(ts: &mut JoinSet<Result<(), WingmateInitError>>, cfg: &config::Config, cancel: CancellationToken)
-> Result<(), WingmateInitError> {
for c_ in cfg.get_cron_iter() {
let shell = cfg.get_shell().ok_or::<WingmateInitError>(WingmateInitError::NoShellAvailable)?;
let cron = c_.clone();
let cancel = cancel.clone();
ts.spawn(async move {
if cron.day_of_month != config::CronTimeFieldSpec::Any
&& cron.day_of_week != config::CronTimeFieldSpec::Any {
return Err(WingmateInitError::CronConfig { source: CronConfigError::ClashingConfig });
}
// let cron = cron.clone();
let mut last_running: Option<OffsetDateTime> = None;
'continuous: loop {
let cron = cron.clone();
time_calc::wait_calc(cron.clone(), &last_running);
select! {
_ = cancel.cancelled() => {
break 'continuous;
}
}
}
Ok(())
});
}
Ok(())
}

View File

@@ -0,0 +1,77 @@
use std::time::Duration;
use anyhow::Context;
use time::OffsetDateTime;
use crate::init::config::{Crontab,CronTimeFieldSpec};
use crate::init::error;
const MINUTE: i8 = 0;
const HOUR: i8 = 1;
const DAY_OF_MONTH: i8 = 2;
const MONTH: i8 = 3;
const DAY_OF_WEEK: i8 = 4;
struct CronField {
spec: CronTimeFieldSpec,
tag: i8,
}
fn convert_cron_spec_to_vec(cron: Crontab) -> Vec<CronField> {
let mut res: Vec<CronField> = Vec::with_capacity(5);
res.push(CronField { spec: cron.minute, tag: MINUTE });
res.push(CronField { spec: cron.hour, tag: HOUR });
res.push(CronField { spec: cron.day_of_month, tag: DAY_OF_MONTH });
res.push(CronField { spec: cron.month, tag: MONTH });
res.push(CronField { spec: cron.day_of_week, tag: DAY_OF_WEEK });
res
}
pub fn wait_calc(cron: Crontab, last_running: &Option<OffsetDateTime>) -> Result<Duration, error::CronConfigError> {
let local_clock = OffsetDateTime::now_local()
.context("getting current time in local timezone")
.map_err(|e| { error::CronConfigError::Other { source: e } })?;
match last_running {
Some(t) => {
let vec_cron = convert_cron_spec_to_vec(cron);
for vc in vec_cron {
}
// match cron.minute {
// CronTimeFieldSpec::Any => {},
// CronTimeFieldSpec::Every(x) => {},
// CronTimeFieldSpec::Exact(x) => {},
// CronTimeFieldSpec::MultiOccurrence(v) => {}
// }
// match cron.hour {
// CronTimeFieldSpec::Any => {},
// CronTimeFieldSpec::Every(x) => {},
// CronTimeFieldSpec::Exact(x) => {},
// CronTimeFieldSpec::MultiOccurrence(v) => {}
// }
// match cron.day_of_month {
// CronTimeFieldSpec::Any => {},
// CronTimeFieldSpec::Every(x) => {},
// CronTimeFieldSpec::Exact(x) => {},
// CronTimeFieldSpec::MultiOccurrence(v) => {}
// }
// match cron.month {
// CronTimeFieldSpec::Any => {},
// CronTimeFieldSpec::Every(x) => {},
// CronTimeFieldSpec::Exact(x) => {},
// CronTimeFieldSpec::MultiOccurrence(v) => {}
// }
// match cron.day_of_week {
// CronTimeFieldSpec::Any => {},
// CronTimeFieldSpec::Every(x) => {},
// CronTimeFieldSpec::Exact(x) => {},
// CronTimeFieldSpec::MultiOccurrence(v) => {}
// }
},
None => {
}
}
Ok(Duration::from_secs(1)) //PLACEHOLDER
}

View File

@@ -51,6 +51,12 @@ pub enum WingmateInitError {
source: tokio::task::JoinError,
},
#[error("cron config")]
CronConfig {
#[source]
source: CronConfigError,
},
#[error("tripped over")]
Other {
#[source]
@@ -58,6 +64,21 @@ pub enum WingmateInitError {
}
}
#[derive(Error,Debug)]
pub enum CronConfigError {
#[error("setting day of week and day of month at the same time will lead to unexpected behavior")]
ClashingConfig,
#[error("when setting time for higher order, the smallest (minute) muste be set")]
MissingMinute,
#[error("something went wrong")]
Other {
#[source]
source: anyhow::Error,
}
}
#[derive(Error,Debug)]
pub enum CronParseError {
#[error("invalid cron syntax: {}", .0)]