Compare commits
9 Commits
6e60f58145
...
298345a75a
Author | SHA1 | Date |
---|---|---|
Suyono | 298345a75a | |
Suyono | 323578e3e3 | |
Suyono | 4195eadc9a | |
Suyono | b4f81ccf30 | |
Suyono | 64fa76d39b | |
Suyono | 342ac49bea | |
Suyono | 212ae4847a | |
Suyono | 05dde284a7 | |
Suyono | 5a0c901281 |
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/coba.txt
|
|
@ -86,6 +86,15 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.29"
|
||||
|
@ -98,6 +107,17 @@ version = "0.3.29"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.0"
|
||||
|
@ -110,6 +130,12 @@ version = "0.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -132,6 +158,12 @@ dependencies = [
|
|||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
|
@ -179,6 +211,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.1"
|
||||
|
@ -217,6 +258,18 @@ version = "0.2.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
|
@ -235,6 +288,36 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
|
@ -285,6 +368,26 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
|
@ -294,6 +397,17 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplelog"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acee08041c5de3d5048c8b3f6f13fafb3026b24ba43c6a695a0c76179b844369"
|
||||
dependencies = [
|
||||
"log",
|
||||
"termcolor",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
|
@ -321,6 +435,15 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
|
@ -341,6 +464,37 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.34.0"
|
||||
|
@ -396,6 +550,37 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -468,9 +653,13 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nix",
|
||||
"rand",
|
||||
"regex",
|
||||
"simplelog",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
|
19
Cargo.toml
19
Cargo.toml
|
@ -5,11 +5,28 @@ edition = "2021"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "wmtest-helper-dummy"
|
||||
path = "src/bin/test-helper/dummy.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wmtest-helper-spawner"
|
||||
path = "src/bin/test-helper/spawner.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wmtest-helper-log"
|
||||
path = "src/bin/test-helper/log.rs"
|
||||
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
lazy_static = "1.4.0"
|
||||
nix = {version = "0.27.1", features = ["process", "signal", "fs"]}
|
||||
log = { version = "0.4.20", features = ["std"]}
|
||||
nix = { version = "0.27.1", features = ["process", "signal", "fs"]}
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.2"
|
||||
simplelog = "0.12.1"
|
||||
thiserror = "1.0.50"
|
||||
time = { version = "0.3.30", features = ["local-offset", "macros"]}
|
||||
tokio = { version = "1.34.0", features = ["full"] }
|
||||
tokio-util = "0.7.10"
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use std::{env, thread, time};
|
||||
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let myi: u64;
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() > 1 {
|
||||
myi = args[1].parse().unwrap();
|
||||
thread::sleep(time::Duration::from_secs(myi));
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("invalid arguments").into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
use std::env;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() == 3 {
|
||||
let file = fs::OpenOptions::new().create(true).append(true).open(&args[1])?;
|
||||
let mut buf = io::BufWriter::new(file);
|
||||
let local_time = OffsetDateTime::now_local()?;
|
||||
buf.write_all(format!("{} {}\n", local_time, &args[2]).as_bytes())?;
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("invalid argument").into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#[macro_use] extern crate log;
|
||||
extern crate simplelog;
|
||||
|
||||
use simplelog::*;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fs::OpenOptions;
|
||||
use std::process::Command;
|
||||
use std::env;
|
||||
use rand::Rng;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let log_path = env::var("LOG_PATH")?;
|
||||
let file = OpenOptions::new().append(true).create(true).open(log_path)?;
|
||||
WriteLogger::init(LevelFilter::Debug, Config::default(), file)?;
|
||||
|
||||
if args.len() > 1 {
|
||||
let x: u64 = args[1].parse()?;
|
||||
for _i in 0..x {
|
||||
let sleep_time = rng.gen_range(10..20);
|
||||
info!("starting wmtest-helper-dummy {}", &sleep_time);
|
||||
let child = Command::new("/usr/local/bin/wmtest-helper-dummy").arg(format!("{}", sleep_time)).spawn();
|
||||
if let Err(e) = child {
|
||||
error!("error spawning child: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
let pause_time = rng.gen_range(5..10);
|
||||
info!("going to sleep for {}", &pause_time);
|
||||
std::thread::sleep(std::time::Duration::from_secs(pause_time));
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("invalid arguments").into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -27,6 +27,6 @@ pub async fn start() -> Result<(), error::WingmateInitError> {
|
|||
}
|
||||
|
||||
let config = config::Config::find(vec_search)?;
|
||||
dbg!(&config);
|
||||
// dbg!(&config);
|
||||
daemon::start(config).await
|
||||
}
|
|
@ -3,11 +3,14 @@ use std::env;
|
|||
use std::path::PathBuf;
|
||||
use std::io::{BufReader, BufRead};
|
||||
use crate::init::error as wingmate_error;
|
||||
use anyhow::anyhow;
|
||||
use nix::unistd::{access, AccessFlags};
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use anyhow::Context;
|
||||
|
||||
pub const MAX_TERM_WAIT_TIME_SECS: u64 = 5;
|
||||
|
||||
const CRON_REGEX_STR: &'static str = r"^\s*(?P<minute>\S+)\s+(?P<hour>\S+)\s+(?P<dom>\S+)\s+(?P<month>\S+)\s+(?P<dow>\S+)\s+(?P<command>\S.*\S)\s*$";
|
||||
const MINUTE: &'static str = "minute";
|
||||
const HOUR: &'static str = "hour";
|
||||
|
@ -30,8 +33,7 @@ pub enum Command {
|
|||
pub enum CronTimeFieldSpec {
|
||||
Any,
|
||||
Exact(u8),
|
||||
MultiOccurrence(Vec<u8>),
|
||||
Every(u8)
|
||||
MultiOccurrence(Vec<u8>)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -124,7 +126,7 @@ impl Config {
|
|||
let mut match_str = cap.name(MINUTE).ok_or::<wingmate_error::CronParseError>(
|
||||
wingmate_error::CronParseError::FieldMatch { cron_line: String::from(&l), field_name: String::from(MINUTE) }
|
||||
)?;
|
||||
let minute = Self::to_cron_time_field_spec(&match_str).map_err(|e| {
|
||||
let minute = Self::to_cron_time_field_spec(&match_str, 60u8).map_err(|e| {
|
||||
wingmate_error::CronParseError::Parse {
|
||||
source: e,
|
||||
cron_line: String::from(&l),
|
||||
|
@ -136,7 +138,7 @@ impl Config {
|
|||
match_str = cap.name(HOUR).ok_or::<wingmate_error::CronParseError>(
|
||||
wingmate_error::CronParseError::FieldMatch { cron_line: String::from(&l), field_name: String::from(HOUR) }
|
||||
)?;
|
||||
let hour = Self::to_cron_time_field_spec(&match_str).map_err(|e| {
|
||||
let hour = Self::to_cron_time_field_spec(&match_str, 24u8).map_err(|e| {
|
||||
wingmate_error::CronParseError::Parse {
|
||||
source: e,
|
||||
cron_line: String::from(&l),
|
||||
|
@ -148,7 +150,7 @@ impl Config {
|
|||
match_str = cap.name(DAY_OF_MONTH_ABBRV).ok_or::<wingmate_error::CronParseError>(
|
||||
wingmate_error::CronParseError::FieldMatch { cron_line: String::from(&l), field_name: String::from(DAY_OF_MONTH) }
|
||||
)?;
|
||||
let dom = Self::to_cron_time_field_spec(&match_str).map_err(|e| {
|
||||
let dom = Self::to_cron_time_field_spec(&match_str, 31u8).map_err(|e| {
|
||||
wingmate_error::CronParseError::Parse {
|
||||
source: e,
|
||||
cron_line: String::from(&l),
|
||||
|
@ -160,7 +162,7 @@ impl Config {
|
|||
match_str = cap.name(MONTH).ok_or::<wingmate_error::CronParseError>(
|
||||
wingmate_error::CronParseError::FieldMatch { cron_line: String::from(&l), field_name: String::from(MONTH) }
|
||||
)?;
|
||||
let month = Self::to_cron_time_field_spec(&match_str).map_err(|e| {
|
||||
let month = Self::to_cron_time_field_spec(&match_str, 12u8).map_err(|e| {
|
||||
wingmate_error::CronParseError::Parse {
|
||||
source: e,
|
||||
cron_line: String::from(&l),
|
||||
|
@ -172,7 +174,7 @@ impl Config {
|
|||
match_str = cap.name(DAY_OF_WEEK_ABBRV).ok_or::<wingmate_error::CronParseError>(
|
||||
wingmate_error::CronParseError::FieldMatch { cron_line: String::from(&l), field_name: String::from(DAY_OF_WEEK) }
|
||||
)?;
|
||||
let dow = Self::to_cron_time_field_spec(&match_str).map_err(|e| {
|
||||
let dow = Self::to_cron_time_field_spec(&match_str, 7u8).map_err(|e| {
|
||||
wingmate_error::CronParseError::Parse {
|
||||
source: e,
|
||||
cron_line: String::from(&l),
|
||||
|
@ -200,26 +202,41 @@ impl Config {
|
|||
Ok(ret_vec)
|
||||
}
|
||||
|
||||
fn to_cron_time_field_spec(match_str: ®ex::Match) -> Result<CronTimeFieldSpec, anyhow::Error> {
|
||||
fn to_cron_time_field_spec(match_str: ®ex::Match, max: u8) -> Result<CronTimeFieldSpec, anyhow::Error> {
|
||||
let field = match_str.as_str();
|
||||
|
||||
if field == "*" {
|
||||
return Ok(CronTimeFieldSpec::Any);
|
||||
} else if field.starts_with("*/") {
|
||||
let every = field[2..].parse::<u8>().context("parsing on field matching \"every\" pattern")?;
|
||||
return Ok(CronTimeFieldSpec::Every(every));
|
||||
if every >= max {
|
||||
return Err(anyhow!("invalid value {}", every));
|
||||
}
|
||||
let mut next_value = every;
|
||||
let mut multi: Vec<u8> = Vec::new();
|
||||
while next_value < max {
|
||||
multi.push(next_value);
|
||||
next_value += every;
|
||||
}
|
||||
return Ok(CronTimeFieldSpec::MultiOccurrence(multi));
|
||||
} else if field.contains(",") {
|
||||
let multi: Vec<&str> = field.split(",").collect();
|
||||
let mut multi_occurrence: Vec<u8> = Vec::new();
|
||||
|
||||
for m in multi {
|
||||
let ur = m.parse::<u8>().context("parsing on field matching \"multi occurrence\" pattern")?;
|
||||
if ur >= max {
|
||||
return Err(anyhow!("invalid value {}", field));
|
||||
}
|
||||
multi_occurrence.push(ur);
|
||||
}
|
||||
|
||||
return Ok(CronTimeFieldSpec::MultiOccurrence(multi_occurrence));
|
||||
} else {
|
||||
let n = field.parse::<u8>().context("parsing on field matching \"exact\" pattern")?;
|
||||
if n >= max {
|
||||
return Err(anyhow!("invalid value {}", n));
|
||||
}
|
||||
return Ok(CronTimeFieldSpec::Exact(n));
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +283,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 +303,97 @@ 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 CronTimeFieldSpec {
|
||||
pub fn is_match(&self, current: u8) -> bool {
|
||||
match self {
|
||||
Self::Any => { return true; },
|
||||
Self::Exact(x) => { return *x == current; },
|
||||
Self::MultiOccurrence(v) => {
|
||||
for i in v {
|
||||
if *i == current {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for CronTimeFieldSpec {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Any => Self::Any,
|
||||
Self::Exact(x) => Self::Exact(*x),
|
||||
Self::MultiOccurrence(x) => {
|
||||
Self::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::Exact(x) => { lhs = CronFieldCmpHelper(1, *x, None); }
|
||||
CronTimeFieldSpec::MultiOccurrence(v) => { lhs = CronFieldCmpHelper(1, 0, Some(v)); }
|
||||
}
|
||||
|
||||
match other {
|
||||
CronTimeFieldSpec::Any => { rhs = CronFieldCmpHelper(0, 0, None); }
|
||||
CronTimeFieldSpec::Exact(x) => { rhs = CronFieldCmpHelper(1, *x, None); }
|
||||
CronTimeFieldSpec::MultiOccurrence(v) => { rhs = CronFieldCmpHelper(2, 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
|
||||
}
|
||||
}
|
|
@ -3,9 +3,13 @@ mod waiter;
|
|||
mod starter;
|
||||
mod constants;
|
||||
|
||||
use tokio::{select, pin};
|
||||
use tokio::task::JoinSet;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration,Instant};
|
||||
use nix::sys::signal::{Signal, kill};
|
||||
use nix::unistd::Pid;
|
||||
use crate::init::config;
|
||||
use crate::init::error as wmerr;
|
||||
use crate::init::error::WingmateInitError;
|
||||
|
@ -16,17 +20,24 @@ pub async fn start(cfg: config::Config) -> Result<(), WingmateInitError> {
|
|||
|
||||
let sighandler_cancel = CancellationToken::new();
|
||||
let waiter_cancel_sighandler = sighandler_cancel.clone();
|
||||
let signal_pump_stop = 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 signal_pump_start = cancel.clone();
|
||||
|
||||
let mut set: JoinSet<Result<(), wmerr::WingmateInitError>> = JoinSet::new();
|
||||
set.spawn(async move {
|
||||
signal_pump(signal_pump_start, signal_pump_stop).await
|
||||
});
|
||||
|
||||
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 || {
|
||||
|
@ -47,7 +58,6 @@ pub async fn start(cfg: config::Config) -> Result<(), WingmateInitError> {
|
|||
return Err(ev);
|
||||
}
|
||||
}
|
||||
// return Err(ev as Box<dyn error::Error>);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
|
@ -57,5 +67,48 @@ pub async fn start(cfg: config::Config) -> Result<(), WingmateInitError> {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn signal_pump(start: CancellationToken, stop: CancellationToken) -> Result<(), WingmateInitError> {
|
||||
const TERM_MODE: u8 = 0;
|
||||
const KILL_MODE: u8 = 1;
|
||||
const ALL_CHILDREN_PID: i32 = -1;
|
||||
|
||||
start.cancelled().await;
|
||||
|
||||
let stop_time = Instant::now();
|
||||
let mut wait_time_millis: u64 = 100;
|
||||
let mut mode = TERM_MODE;
|
||||
|
||||
'signal: loop {
|
||||
let stop = stop.clone();
|
||||
let s = tokio::time::sleep(Duration::from_millis(wait_time_millis));
|
||||
pin!(s);
|
||||
|
||||
select! {
|
||||
() = &mut s => {
|
||||
if mode == TERM_MODE {
|
||||
if let Err(e) = kill(Pid::from_raw(ALL_CHILDREN_PID), Signal::SIGTERM) {
|
||||
eprintln!("daemon: sending TERM signal got {}", e);
|
||||
}
|
||||
} else {
|
||||
if let Err(e) = kill(Pid::from_raw(ALL_CHILDREN_PID), Signal::SIGKILL) {
|
||||
eprintln!("daemon: sending KILL signal got {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
let time_peek = Instant::now();
|
||||
if time_peek.saturating_duration_since(stop_time).as_secs() >= config::MAX_TERM_WAIT_TIME_SECS && mode == TERM_MODE {
|
||||
wait_time_millis = 10;
|
||||
mode = KILL_MODE;
|
||||
}
|
||||
}
|
||||
_ = stop.cancelled() => {
|
||||
break 'signal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,19 +1,25 @@
|
|||
use time::error::IndeterminateOffset;
|
||||
use tokio::task::JoinSet;
|
||||
use tokio::process::{Command, Child};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio::select;
|
||||
use tokio::io::Result as tokio_result;
|
||||
use tokio::time::sleep;
|
||||
use tokio::time::{sleep, interval};
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
use std::process::ExitStatus;
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
use nix::errno::Errno;
|
||||
use nix::unistd::Pid;
|
||||
use anyhow::Context;
|
||||
use anyhow::{Context, anyhow};
|
||||
use time::{OffsetDateTime, Duration as TimeDur, Weekday, UtcOffset};
|
||||
use crate::init::config;
|
||||
use crate::init::error::WingmateInitError;
|
||||
use crate::init::error::{WingmateInitError, CronConfigError};
|
||||
|
||||
|
||||
const CRON_TRIGGER_WAIT_SECS: u64 = 20;
|
||||
const ENV_UTC_OFFSET: &'static str = "WINGMATE_TIME_OFFSET";
|
||||
|
||||
pub fn start_services(ts: &mut JoinSet<Result<(), WingmateInitError>>, cfg: &config::Config, cancel: CancellationToken)
|
||||
-> Result<(), WingmateInitError> {
|
||||
|
||||
|
@ -51,7 +57,7 @@ pub fn start_services(ts: &mut JoinSet<Result<(), WingmateInitError>>, cfg: &con
|
|||
match kill(Pid::from_raw(id as i32), Some(Signal::SIGTERM)) {
|
||||
Ok(_) => {
|
||||
select! {
|
||||
_ = sleep(Duration::from_secs(5)) => {
|
||||
_ = sleep(Duration::from_secs(config::MAX_TERM_WAIT_TIME_SECS)) => {
|
||||
child.kill().await.expect("failed to kill process");
|
||||
},
|
||||
result = child.wait() => {
|
||||
|
@ -102,7 +108,185 @@ 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> {
|
||||
|
||||
dbg!("cron: starting");
|
||||
for c_ in cfg.get_cron_iter() {
|
||||
let cron = c_.clone();
|
||||
let in_loop_cancel = cancel.clone();
|
||||
dbg!("cron: item", c_);
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
dbg!("cron: async task spawned");
|
||||
|
||||
let cron = cron.clone();
|
||||
let mut cron_interval = interval(Duration::from_secs(CRON_TRIGGER_WAIT_SECS));
|
||||
let mut cron_procs: JoinSet<Result<(), WingmateInitError>> = JoinSet::new();
|
||||
let mut last_running: Option<OffsetDateTime> = None;
|
||||
'continuous: loop {
|
||||
let cron = cron.clone();
|
||||
let cron_proc_cancel = in_loop_cancel.clone();
|
||||
dbg!("cron: single: in loop", &cron.command);
|
||||
|
||||
let mut flag = true;
|
||||
|
||||
let tr: Result<OffsetDateTime, IndeterminateOffset>;
|
||||
if let Ok(offset) = env::var(ENV_UTC_OFFSET) {
|
||||
if let Ok(i_off) = offset.parse::<i8>() {
|
||||
let utc_time = OffsetDateTime::now_utc().to_offset(UtcOffset::from_hms(i_off, 0, 0).unwrap());
|
||||
tr = Ok(utc_time);
|
||||
} else {
|
||||
tr = OffsetDateTime::now_local();
|
||||
}
|
||||
} else {
|
||||
tr = OffsetDateTime::now_local();
|
||||
}
|
||||
// let tr = OffsetDateTime::now_local();
|
||||
if let Ok(local_time) = tr {
|
||||
dbg!("cron: current local time", &local_time);
|
||||
if let Some(last) = last_running {
|
||||
dbg!("cron: last runing instance", &last);
|
||||
if local_time - last < TimeDur::minutes(1) {
|
||||
flag = false;
|
||||
} else {
|
||||
flag = flag && cron.minute.is_match(local_time.minute()) &&
|
||||
cron.hour.is_match(local_time.hour()) &&
|
||||
cron.day_of_month.is_match(local_time.day()) &&
|
||||
cron.day_of_week.is_match(weekday_map(local_time.weekday()));
|
||||
}
|
||||
} else {
|
||||
flag = flag && cron.minute.is_match(local_time.minute()) &&
|
||||
cron.hour.is_match(local_time.hour()) &&
|
||||
cron.day_of_month.is_match(local_time.day()) &&
|
||||
cron.day_of_week.is_match(weekday_map(local_time.weekday()));
|
||||
}
|
||||
|
||||
if flag {
|
||||
dbg!("cron: timing: hit: {}", &cron.command);
|
||||
last_running = Some(local_time);
|
||||
cron_procs.spawn(async move {
|
||||
run_cron_command(cron.command.clone(), cron_proc_cancel).await
|
||||
});
|
||||
}
|
||||
} else {
|
||||
dbg!("cron: unexpected error");
|
||||
if let Err(e) = tr {
|
||||
dbg!(e);
|
||||
}
|
||||
}
|
||||
|
||||
if cron_procs.is_empty() {
|
||||
select! {
|
||||
_ = in_loop_cancel.cancelled() => {
|
||||
break 'continuous;
|
||||
},
|
||||
_ = cron_interval.tick() => {},
|
||||
}
|
||||
} else {
|
||||
'task: while !cron_procs.is_empty() {
|
||||
select! {
|
||||
opt_res = cron_procs.join_next() => {
|
||||
if let Some(res) = opt_res {
|
||||
if let Err(e) = res {
|
||||
eprintln!("running cron got problem {:?}", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ = in_loop_cancel.cancelled() => {
|
||||
while let Some(res) = cron_procs.join_next().await {
|
||||
if let Err(e) = res {
|
||||
eprintln!("running cron got problem {:?}", e);
|
||||
}
|
||||
}
|
||||
break 'continuous;
|
||||
},
|
||||
_ = cron_interval.tick() => {
|
||||
break 'task;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn weekday_map(wd: Weekday) -> u8 {
|
||||
match wd {
|
||||
Weekday::Sunday => 0,
|
||||
Weekday::Monday => 1,
|
||||
Weekday::Tuesday => 2,
|
||||
Weekday::Wednesday => 3,
|
||||
Weekday::Thursday => 4,
|
||||
Weekday::Friday => 5,
|
||||
Weekday::Saturday => 6
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_cron_command(command: String, cancel: CancellationToken) -> Result<(), WingmateInitError> {
|
||||
let mut args: Vec<&str> = Vec::new();
|
||||
for part in command.split(' ') {
|
||||
if part.len() > 0 {
|
||||
args.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
dbg!("cron: in running command");
|
||||
if args.is_empty() {
|
||||
return Err(WingmateInitError::Other { source: anyhow!("parsed as empty: {}", command) });
|
||||
}
|
||||
|
||||
let cmd = args.swap_remove(0);
|
||||
let mut child: Child;
|
||||
if args.is_empty() {
|
||||
child = Command::new(cmd).spawn().map_err(|e| {
|
||||
WingmateInitError::SpawnError { source: e, message: command }
|
||||
})?;
|
||||
} else {
|
||||
child = Command::new(cmd).args(args.as_slice()).spawn().map_err(|e| {
|
||||
WingmateInitError::SpawnError { source: e, message: command }
|
||||
})?;
|
||||
}
|
||||
|
||||
select! {
|
||||
_ = cancel.cancelled() => {
|
||||
if let Some(id) = child.id() {
|
||||
match kill(Pid::from_raw(id as i32), Some(Signal::SIGTERM)) {
|
||||
Ok(_) => {
|
||||
if let Err(e) = result_match(child.wait().await) {
|
||||
return Err(WingmateInitError::ChildExit { source: e });
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
match e {
|
||||
Errno::ESRCH => {
|
||||
return Err(WingmateInitError::ChildNotFound);
|
||||
},
|
||||
_ => {
|
||||
return Err(WingmateInitError::FromNix { source: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
result = child.wait() => {
|
||||
if let Err(e) = result_match(result) {
|
||||
return Err(WingmateInitError::ChildExit { source: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -51,6 +51,18 @@ pub enum WingmateInitError {
|
|||
source: tokio::task::JoinError,
|
||||
},
|
||||
|
||||
#[error("cron config")]
|
||||
CronConfig {
|
||||
#[source]
|
||||
source: CronConfigError,
|
||||
},
|
||||
|
||||
#[error("from nix")]
|
||||
FromNix {
|
||||
#[source]
|
||||
source: nix::Error,
|
||||
},
|
||||
|
||||
#[error("tripped over")]
|
||||
Other {
|
||||
#[source]
|
||||
|
@ -58,6 +70,18 @@ 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("something went wrong")]
|
||||
// Other {
|
||||
// #[source]
|
||||
// source: anyhow::Error,
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Error,Debug)]
|
||||
pub enum CronParseError {
|
||||
#[error("invalid cron syntax: {}", .0)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
mod init;
|
||||
|
||||
use std::error;
|
||||
use wingmate_rs::init;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn error::Error>> {
|
Loading…
Reference in New Issue