diff --git a/Cargo.lock b/Cargo.lock index 73ea1a301ad92922d8d59eb051641bf1606b0979..054c33ff6f7402d299d0bfe6b9c14ee1cf30a745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,7 @@ name = "basic" version = "0.5.1" dependencies = [ "bitflags", + "chrono", "constants", "fnmatch-sys", "libc", @@ -1884,6 +1885,7 @@ dependencies = [ "siphasher", "socket", "target", + "timer", "unit_parser", "walkdir", ] @@ -1964,6 +1966,23 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "timer" +version = "0.5.0" +dependencies = [ + "basic", + "chrono", + "core", + "event", + "log 0.5.1", + "macros", + "nix 0.24.3", + "once_cell", + "rand", + "serde", + "unit_parser", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/core/coms/service/src/mng.rs b/core/coms/service/src/mng.rs index eaf5469447b67e426a136f868e3d8b0025114d7b..39b0fd7864d75a7167d420011007f1392a4a1483 100644 --- a/core/coms/service/src/mng.rs +++ b/core/coms/service/src/mng.rs @@ -870,6 +870,9 @@ impl ServiceMng { // todo!() // trigger the unit the dependency trigger_by + self.comm + .um() + .trigger_notify(&self.comm.owner().unwrap().id()); let os = service_state_to_unit_state(self.config.service_type(), original_state); let ns = service_state_to_unit_state(self.config.service_type(), state); diff --git a/core/coms/timer/Cargo.toml b/core/coms/timer/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..3244dff33e6d892d7c6e9c55451e17e91eb21944 --- /dev/null +++ b/core/coms/timer/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "timer" +version = "0.5.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +basic = { path = "../../../libs/basic", default-features = false, features = [ + "machine" +] } + +log = { path = "../../../libs/log" } +macros = { path = "../../../libs/macros" } +core = { path = "../../libcore", default-features = false } +event = { path = "../../../libs/event" } +unit_parser = { path = "../../../libs/unit_parser" } + +nix = { version = "0.24", default-features = false} +once_cell = { version = "1.8.0", default-features = false } +serde = { version = "1.0.130", default-features = false } +rand = "0.8.5" +chrono = "0.4.26" diff --git a/core/coms/timer/src/base.rs b/core/coms/timer/src/base.rs new file mode 100644 index 0000000000000000000000000000000000000000..a5f06acb34393e3d88ffb032ef110629a3ba3936 --- /dev/null +++ b/core/coms/timer/src/base.rs @@ -0,0 +1,13 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +#[cfg(feature = "plugin")] +pub(super) const PLUGIN_NAME: &str = "TimerUnit"; diff --git a/core/coms/timer/src/comm.rs b/core/coms/timer/src/comm.rs new file mode 100644 index 0000000000000000000000000000000000000000..5bfafc9622d3195fdebc2aad3c202dd9e4705cf8 --- /dev/null +++ b/core/coms/timer/src/comm.rs @@ -0,0 +1,202 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! The method provided by the public object needs to be called. +//! +use core::rel::Reliability; +use core::unit::{UmIf, UnitBase}; +use once_cell::sync::Lazy; +use std::cell::RefCell; +use std::rc::{Rc, Weak}; +use std::sync::{Arc, RwLock}; + +use crate::rentry::{SectionTimer, TimerRe, TimerResult, TimerState}; + +pub(crate) struct TimerUnitComm { + owner: RefCell>>, + umcomm: Arc, +} + +impl TimerUnitComm { + pub(super) fn new() -> Self { + TimerUnitComm { + owner: RefCell::new(None), + umcomm: TimerUmComm::get_instance(), + } + } + + pub(super) fn attach_unit(&self, unit: Rc) { + self.owner.replace(Some(Rc::downgrade(&unit))); + } + + pub(super) fn attach_um(&self, um: Rc) { + self.umcomm.attach_um(um) + } + + pub(super) fn attach_reli(&self, reli: Rc) { + self.umcomm.attach_reli(reli) + } + + pub(super) fn owner(&self) -> Option> { + if let Some(ref unit) = *self.owner.borrow() { + unit.upgrade() + } else { + None + } + } + + pub(super) fn um(&self) -> Rc { + self.umcomm.um() + } + + pub(super) fn _reli(&self) -> Rc { + self.umcomm._reli() + } + + pub(super) fn rentry(&self) -> Rc { + self.umcomm.rentry() + } + + pub(super) fn rentry_conf_insert(&self, timer: &SectionTimer, service: String) { + if let Some(u) = self.owner() { + self.rentry().conf_insert(&u.id(), timer, service) + } + } + + pub(super) fn rentry_conf_get(&self) -> Option<(SectionTimer, String)> { + self.owner().map(|u| self.rentry().conf_get(&u.id()))? + } + + #[allow(clippy::too_many_arguments)] + pub(super) fn rentry_mng_insert( + &self, + state: TimerState, + result: TimerResult, + last_trigger_realtime: u64, + last_trigger_monotonic: u64, + ) { + if let Some(u) = self.owner() { + self.rentry().mng_insert( + &u.id(), + state, + result, + last_trigger_realtime, + last_trigger_monotonic, + ) + }; + } + + #[allow(clippy::type_complexity)] + pub(super) fn rentry_mng_get(&self) -> Option<(TimerState, TimerResult, u64, u64)> { + self.owner().map(|u| self.rentry().mng_get(&u.id()))? + } +} + +static TIMER_UM_COMM: Lazy> = Lazy::new(|| { + let comm = TimerUmComm::new(); + Arc::new(comm) +}); + +pub(super) struct TimerUmComm { + data: RwLock, +} + +unsafe impl Send for TimerUmComm {} + +unsafe impl Sync for TimerUmComm {} + +impl TimerUmComm { + pub(super) fn new() -> Self { + TimerUmComm { + data: RwLock::new(TimerUmCommData::new()), + } + } + + pub(super) fn attach_um(&self, um: Rc) { + let mut wdata = self.data.write().unwrap(); + wdata.attach_um(um); + } + + pub(super) fn attach_reli(&self, reli: Rc) { + let mut wdata = self.data.write().unwrap(); + wdata.attach_reli(reli); + } + + pub(super) fn get_instance() -> Arc { + TIMER_UM_COMM.clone() + } + + pub(super) fn _reli(&self) -> Rc { + let rdata = self.data.read().unwrap(); + rdata._reli() + } + + pub(super) fn um(&self) -> Rc { + let rdata = self.data.read().unwrap(); + rdata.um().unwrap() + } + + pub(super) fn rentry(&self) -> Rc { + let rdata = self.data.read().unwrap(); + rdata.rentry() + } +} + +struct TimerUmCommData { + // associated objects + um: Option>, + reli: Weak, + rentry: Option>, +} + +// the declaration "pub(self)" is for identification only. +impl TimerUmCommData { + pub(self) fn new() -> TimerUmCommData { + TimerUmCommData { + um: None, + reli: Weak::new(), + rentry: None, + } + } + + pub(self) fn attach_um(&mut self, um: Rc) { + if self.um.is_none() { + log::debug!("TimerUmComm attach_um action."); + self.um = Some(um) + } + } + + pub(self) fn attach_reli(&mut self, reli: Rc) { + let old = self.reli.clone().upgrade(); + if old.is_none() { + log::debug!("TimerUmComm attach_reli action."); + self.reli = Rc::downgrade(&reli); + self.rentry.replace(Rc::new(TimerRe::new(&reli))); + } + } + + pub(self) fn um(&self) -> Option> { + if let Some(ref um) = self.um { + Some(Rc::clone(um)) + } else { + None + } + } + + pub(self) fn _reli(&self) -> Rc { + self.reli.clone().upgrade().unwrap() + } + + pub(self) fn rentry(&self) -> Rc { + self.rentry.as_ref().cloned().unwrap() + } +} diff --git a/core/coms/timer/src/config.rs b/core/coms/timer/src/config.rs new file mode 100644 index 0000000000000000000000000000000000000000..c0340bb7880869c9c59331fc04f70b883aa9923b --- /dev/null +++ b/core/coms/timer/src/config.rs @@ -0,0 +1,246 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +#![allow(non_snake_case)] +use crate::{ + comm::TimerUnitComm, + rentry::{CalendarSpec, SectionTimer, TimerBase, TimerValue}, +}; +use basic::time_util::USEC_INFINITY; +use core::error::*; +use core::rel::ReStation; +use std::{cell::RefCell, path::PathBuf, rc::Rc}; +use unit_parser::prelude::UnitConfig; + +#[derive(Default, Clone)] +pub struct UnitRef { + source: String, + target: String, +} + +impl UnitRef { + /// + pub fn new() -> Self { + UnitRef { + source: String::new(), + target: String::new(), + } + } + + /// + pub fn set_ref(&mut self, source: String, target: String) { + self.source = source; + self.target = target; + } + + /// + pub fn target(&self) -> String { + self.target.clone() + } +} + +// +#[derive(UnitConfig, Default, Debug)] +pub(crate) struct TimerConfigData { + #[section(must)] + pub Timer: SectionTimer, +} + +impl TimerConfigData { + pub(self) fn new(Timer: SectionTimer) -> TimerConfigData { + TimerConfigData { Timer } + } +} + +pub struct TimerConfig { + // associated objects + comm: Rc, + + // owned objects + /* original */ + data: Rc>, + /* processed */ + timerunitref: RefCell, + pub value: RefCell>, +} + +impl ReStation for TimerConfig { + // no input, no compensate + + // data + fn db_map(&self, reload: bool) { + if reload { + return; + } + if let Some((data, unit)) = self.comm.rentry_conf_get() { + // TImerConfigData + self.data.replace(TimerConfigData::new(data)); + + // UnitRef + self.set_unit_ref(unit); + } + } + + fn db_insert(&self) { + self.comm + .rentry_conf_insert(&self.data.borrow().Timer, self.unit_ref_target()); + } +} + +impl TimerConfig { + pub(super) fn new(commr: &Rc) -> Self { + TimerConfig { + comm: Rc::clone(commr), + data: Rc::new(RefCell::new(TimerConfigData::default())), + timerunitref: RefCell::new(UnitRef::new()), + value: RefCell::new(Vec::new()), + } + } + + pub(super) fn reset(&self) { + self.data.replace(TimerConfigData::default()); + self.timerunitref.replace(UnitRef::new()); + self.db_update(); + } + + fn load_value(&self) { + let timerconf = self.data.borrow().Timer.clone(); + let mut timervalue = self.value.borrow_mut(); + if timerconf.OnCalendar != USEC_INFINITY { + let v = TimerValue::new( + TimerBase::Calendar, + false, + timerconf.OnCalendar, + CalendarSpec::default(), + 0, + ); + timervalue.push(v) + } + + if timerconf.OnActiveSec != USEC_INFINITY { + let v = TimerValue::new( + TimerBase::Active, + false, + timerconf.OnActiveSec, + CalendarSpec::default(), + 0, + ); + timervalue.push(v) + } + + if timerconf.OnBootSec != USEC_INFINITY { + let v = TimerValue::new( + TimerBase::Boot, + false, + timerconf.OnBootSec, + CalendarSpec::default(), + 0, + ); + timervalue.push(v) + } + + if timerconf.OnStartupSec != USEC_INFINITY { + let v = TimerValue::new( + TimerBase::Startup, + false, + timerconf.OnStartupSec, + CalendarSpec::default(), + 0, + ); + timervalue.push(v) + } + + if timerconf.OnUnitActiveSec != USEC_INFINITY { + let v = TimerValue::new( + TimerBase::UnitActive, + false, + timerconf.OnUnitActiveSec, + CalendarSpec::default(), + 0, + ); + timervalue.push(v) + } + + if timerconf.OnUnitInactiveSec != USEC_INFINITY { + let v = TimerValue::new( + TimerBase::UnitInactive, + false, + timerconf.OnUnitInactiveSec, + CalendarSpec::default(), + 0, + ); + timervalue.push(v) + } + } + + pub(super) fn load(&self, paths: Vec, update: bool) -> Result<()> { + let name = paths[0].file_name().unwrap().to_string_lossy().to_string(); + let data = match TimerConfigData::load_config(paths, &name) { + Ok(v) => v, + Err(e) => { + log::error!("Invalid Configuration: {}", e); + return Err(Error::ConfigureError { + msg: format!("Invalid Configuration: {}", e), + }); + } + }; + + // record original configuration + *self.data.borrow_mut() = data; + + // parse and record processed configuration + let ret = self.parse_unit(); + if ret.is_err() { + self.reset(); // fallback + return ret; + } + + self.load_value(); + + if update { + self.db_update(); + } + + Ok(()) + } + + pub(super) fn config_data(&self) -> Rc> { + self.data.clone() + } + + pub(super) fn set_unit_ref(&self, unit: String) { + self.set_ref(unit); + self.db_update(); + } + + pub(super) fn unit_ref_target(&self) -> String { + self.timerunitref.borrow().target() + } + + fn parse_unit(&self) -> Result<()> { + if let Some(unit) = self.config_data().borrow().Timer.Unit.clone() { + if unit.ends_with(".timer") { + log::warn!("Timer Unit must not be end with .timer, ignoring:{}", unit); + return Ok(()); + } + self.set_unit_ref(unit); + } else if let Some(unit) = self.comm.owner() { + self.set_unit_ref(unit.id().replace(".timer", ".service")); + } + Ok(()) + } + + fn set_ref(&self, target: String) { + if let Some(u) = self.comm.owner() { + self.timerunitref.borrow_mut().set_ref(u.id(), target) + }; + } +} diff --git a/core/coms/timer/src/lib.rs b/core/coms/timer/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..ca683ce39b40c27ef5e542b4d956511f7c040c0b --- /dev/null +++ b/core/coms/timer/src/lib.rs @@ -0,0 +1,72 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,WITHOUT WARRANTIES OF ANY +// KIND,EITHER EXPRESS OR IMPLIED,INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT,MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +//! Timer is a kind of sysmaster startup type. It can accelerate startup by creating a socket socket first, and then pulling the corresponding service when the socket socket receives a request. +//! The timer configuration file contains three sections: Unit, Socket, and Install. +//! +//! # Example: +//! ``` toml +//! [Unit] +//! Description="test timer" +//! Documentation="test.timer" +//! +//! [Timer] +//! OnActiveSec=3s +//! +//! [Install] +//! WantedBy="timers.target" +//! ``` +//! `[Timer]` section related configuration +//! AccuracySec +//! +//! Set the trigger precision of the timer +//! +//! OnActiveSec、 OnBootSec、 OnStartupSec、 OnUnitActiveSec、 OnUnitInactiveSec +//! +//! Defines a monotone timer relative to a period of time after a specific point in time +//! +//! OnCalendar +//! +//! Define a calendar timer based on the wallclock +//! +//! RandomizedDelaySec +//! +//! Randomly delay the timer of this unit for a short period of time +//! +//! Unit +//! +//! The matching unit of the timer unit. The default value is the service unit with the same name as the timer unit +//! +//! Persistent +//! +//! WakeSystem +//! +//! Whether to wake up the hibernating system and prevent the system from entering the hibernation state when a timer reaches the timer's trigger time point +//! +//! RemainAfterElapse +//! +//! Whether to maintain the loaded state of the expired timer unit and still allow the query of its status + +pub use {manager::__um_obj_create, unit::__subunit_create_with_params}; + +// dependency: +// timer_base -> timer_rentry -> {timer_comm | timer_config} +// timer_mng -> timer_unit -> timer_manager + +mod base; +mod comm; +mod config; +mod load; +mod manager; +mod mng; +mod rentry; +mod unit; diff --git a/core/coms/timer/src/load.rs b/core/coms/timer/src/load.rs new file mode 100644 index 0000000000000000000000000000000000000000..4e3192c735e5eeb8b29572f173b98c9b9bc72a3f --- /dev/null +++ b/core/coms/timer/src/load.rs @@ -0,0 +1,111 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::{comm::TimerUnitComm, config::TimerConfig}; +use basic::{SHUTDOWN_TARGET, SYSINIT_TARGET, TIMERS_TARGET}; +use core::{ + error::*, + unit::{UnitDependencyMask, UnitRelations, UnitType}, +}; +use std::{path::Path, rc::Rc}; + +pub(super) struct TimerLoad { + config: Rc, + comm: Rc, +} + +impl TimerLoad { + pub(super) fn new(configr: &Rc, commr: &Rc) -> Self { + TimerLoad { + config: configr.clone(), + comm: commr.clone(), + } + } + + pub fn timer_add_extras(&self) -> Result<()> { + log::debug!("timer add extras"); + if self.config.unit_ref_target().is_empty() { + self.load_related_unit(UnitType::UnitService)?; + } + if let Some(owner) = self.comm.owner() { + let um = self.comm.um(); + um.unit_add_two_dependency( + &owner.id(), + UnitRelations::UnitBefore, + UnitRelations::UnitTriggers, + &self.config.unit_ref_target(), + true, + UnitDependencyMask::Implicit, + )?; + } + + self.add_default_dependencies()?; + + Ok(()) + } + + fn load_related_unit(&self, related_type: UnitType) -> Result<()> { + let unit_name = self.comm.owner().map(|u| u.id()); + let suffix = String::from(related_type); + if suffix.is_empty() { + return Err(format!("failed to load related unit {}", suffix).into()); + } + if unit_name.is_none() { + return Err(format!("failed to load related unit {} unit name is none", suffix).into()); + } + let u_name = unit_name.unwrap(); + let stem_name = Path::new(&u_name).file_stem().unwrap().to_str().unwrap(); + let relate_name = format!("{}.{}", stem_name, suffix); + self.config.set_unit_ref(relate_name); + Ok(()) + } + + pub(self) fn add_default_dependencies(&self) -> Result<()> { + let u = match self.comm.owner() { + None => { + return Ok(()); + } + Some(v) => v, + }; + + if !u.default_dependencies() { + return Ok(()); + } + + log::debug!("Adding default dependencies for timer: {}", u.id()); + let um = self.comm.um(); + um.unit_add_dependency( + &u.id(), + UnitRelations::UnitAfter, + TIMERS_TARGET, + true, + UnitDependencyMask::Default, + )?; + um.unit_add_two_dependency( + &u.id(), + UnitRelations::UnitAfter, + UnitRelations::UnitRequires, + SYSINIT_TARGET, + true, + UnitDependencyMask::Default, + )?; + um.unit_add_two_dependency( + &u.id(), + UnitRelations::UnitBefore, + UnitRelations::UnitConflicts, + SHUTDOWN_TARGET, + true, + UnitDependencyMask::Default, + )?; + Ok(()) + } +} diff --git a/core/coms/timer/src/manager.rs b/core/coms/timer/src/manager.rs new file mode 100644 index 0000000000000000000000000000000000000000..6d87c346eec213858ef30bb807f37857b66a1ed3 --- /dev/null +++ b/core/coms/timer/src/manager.rs @@ -0,0 +1,66 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[cfg(feature = "plugin")] +use crate::base::PLUGIN_NAME; +#[cfg(feature = "plugin")] +use constants::LOG_FILE_PATH; + +use super::comm::TimerUmComm; +use core::rel::{ReStation, Reliability}; +use core::unit::{UmIf, UnitManagerObj, UnitMngUtil}; +use std::rc::Rc; +use std::sync::Arc; +struct TimerManager { + comm: Arc, +} + +// the declaration "pub(self)" is for identification only. +impl TimerManager { + pub(self) fn new() -> TimerManager { + let _comm = TimerUmComm::get_instance(); + TimerManager { + comm: Arc::clone(&_comm), + } + } +} + +impl UnitManagerObj for TimerManager { + // nothing to customize +} + +impl ReStation for TimerManager { + // no input, no compensate + + // no data + + // reload: no external connections, no entry +} + +impl UnitMngUtil for TimerManager { + fn attach_um(&self, um: Rc) { + self.comm.attach_um(um) + } + + fn attach_reli(&self, reli: Rc) { + self.comm.attach_reli(reli); + } +} + +impl Default for TimerManager { + fn default() -> Self { + TimerManager::new() + } +} + +use core::declare_umobj_plugin; +declare_umobj_plugin!(TimerManager, TimerManager::default); diff --git a/core/coms/timer/src/mng.rs b/core/coms/timer/src/mng.rs new file mode 100644 index 0000000000000000000000000000000000000000..6634158aa54f1affbc699b3db37950482ad491a9 --- /dev/null +++ b/core/coms/timer/src/mng.rs @@ -0,0 +1,628 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::{ + comm::TimerUnitComm, + config::TimerConfig, + rentry::{TimerBase, TimerResult, TimerState}, +}; +use basic::{ + fs_util::touch_file, + machine::Machine, + time_util::{ + duml_timestamp_is_set, now_clockid, timespec_load, triple_timestamp_by_clock, usec_add, + usec_shift_clock, DualTimestamp, TripleTimestamp, USEC_INFINITY, + }, + IN_SET, +}; +use core::{ + error::*, + rel::ReStation, + unit::{UnitActiveState, UnitNotifyFlags}, +}; +use event::{EventState, EventType, Events, Source}; +use nix::{ + libc::{ + clockid_t, suseconds_t, time_t, timespec, CLOCK_BOOTTIME_ALARM, CLOCK_MONOTONIC, + CLOCK_REALTIME, + }, + sys::stat, +}; +use rand::Rng; +use std::{ + cell::RefCell, + path::Path, + rc::{Rc, Weak}, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +pub struct TimerMng { + // associated objects + comm: Rc, + config: Rc, + + // owned objects + state: RefCell, + result: RefCell, + last_trigger: RefCell, + next_elapse_monotonic_or_boottime: RefCell, + next_elapse_reltime: RefCell, + stamp_path: String, + on_timezone_change: bool, + on_clock_change: bool, + mt: Rc, + rt: Rc, +} + +impl TimerMng { + pub(crate) fn new( + commr: &Rc, + configr: &Rc, + mtr: &Rc, + rtr: &Rc, + ) -> TimerMng { + TimerMng { + comm: Rc::clone(commr), + config: Rc::clone(configr), + + state: RefCell::new(TimerState::Dead), + result: RefCell::new(TimerResult::Success), + last_trigger: RefCell::new(DualTimestamp::default()), + next_elapse_monotonic_or_boottime: RefCell::new(0), + next_elapse_reltime: RefCell::new(0), + stamp_path: String::new(), + on_timezone_change: false, + on_clock_change: false, + mt: Rc::clone(mtr), + rt: Rc::clone(rtr), + } + } + + fn result(&self) -> TimerResult { + *self.result.borrow() + } + + fn set_result(&self, res: TimerResult) { + *self.result.borrow_mut() = res; + } + + pub(crate) fn state(&self) -> TimerState { + *self.state.borrow() + } + + pub fn get_on_clock_change(&self) -> bool { + self.on_clock_change + } + + pub fn get_on_timezone_change(&self) -> bool { + self.on_timezone_change + } + + pub fn trigger_notify(&self) { + { + let mut v = self.config.value.borrow_mut(); + for i in 0..v.len() { + if IN_SET!( + v[i].timerbase, + TimerBase::UnitActive, + TimerBase::UnitInactive + ) { + v[i].disabled = false; + } + } + } + + match self.state() { + TimerState::Waiting | TimerState::Elapsed => self.enter_waiting(false), + TimerState::Running => { + let trigger_unit_state = self + .comm + .um() + .current_active_state(&self.config.unit_ref_target()); + if IN_SET!( + trigger_unit_state, + UnitActiveState::Failed, + UnitActiveState::InActive + ) { + self.enter_waiting(false); + } + } + TimerState::StateMax + | TimerState::StateInvalid + | TimerState::Dead + | TimerState::Failed => {} + } + } + + fn set_state(&self, state: TimerState) { + let original_state = self.state(); + *self.state.borrow_mut() = state; + log::debug!( + "original state: {:?}, change to: {:?}", + original_state, + state + ); + + if self.state() != TimerState::Waiting { + *self.next_elapse_monotonic_or_boottime.borrow_mut() = USEC_INFINITY; + *self.next_elapse_reltime.borrow_mut() = USEC_INFINITY; + } + + if let Some(u) = self.comm.owner() { + u.notify( + original_state.to_unit_active_state(), + state.to_unit_active_state(), + UnitNotifyFlags::RELOAD_FAILURE, + ) + } + } + + pub(crate) fn current_active_state(&self) -> UnitActiveState { + self.state().to_unit_active_state() + } + + pub fn reset_failed(&self) { + if self.state() == TimerState::Failed { + self.set_state(TimerState::Dead) + } + self.set_result(TimerResult::Success) + } + + pub fn start(&self) -> Result { + assert!(IN_SET!(self.state(), TimerState::Dead, TimerState::Failed)); + + if !self + .comm + .um() + .load_unit_success(&self.config.unit_ref_target()) + { + return Ok(false); + } + + { + let mut v = self.config.value.borrow_mut(); + for i in 0..v.len() { + if v[i].timerbase == TimerBase::Active { + v[i].disabled = false; + } + } + } + + if !self.stamp_path.is_empty() { + match stat::stat(Path::new(&self.stamp_path)) { + Ok(st) => { + let system_time = SystemTime::UNIX_EPOCH + + Duration::from_secs(st.st_mtime.try_into().unwrap()); + let duration = system_time.duration_since(UNIX_EPOCH).unwrap(); + let ts = timespec { + tv_sec: duration.as_secs() as time_t, + tv_nsec: duration.subsec_nanos() as suseconds_t * 1000, + }; + let ft = timespec_load(ts); + let now_ts_reltime = now_clockid(CLOCK_REALTIME); + if ft < now_ts_reltime { + self.last_trigger.borrow_mut().realtime = ft; + } else { + log::warn!( + "Not using persistent file timestamp {:?} as it is in the feature", + ft + ); + } + } + + Err(e) => { + if e == Errno::ENOENT { + let r = touch_file(&self.stamp_path, true, None, None, None).unwrap(); + if !r { + log::warn!("Failed to touch file!"); + } + } + } + }; + } + + self.set_result(TimerResult::Success); + self.enter_waiting(false); + + Ok(true) + } + + pub fn stop(&self) -> Result { + assert!(IN_SET!( + self.state(), + TimerState::Waiting, + TimerState::Running, + TimerState::Elapsed + )); + + self.enter_dead(TimerResult::Success); + + Ok(true) + } + + fn timer_monotonic_clock(&self) -> clockid_t { + if self.config.config_data().borrow().Timer.WakeSystem { + CLOCK_BOOTTIME_ALARM + } else { + CLOCK_MONOTONIC + } + } + + fn get_randomized_delay_sec(&self, time: u64) -> u64 { + if time == 0 { + return 0; + } + rand::thread_rng().gen_range(0..time) + } + + pub fn enter_waiting(&self, time_change: bool) { + let mut found_monotonic = false; + let found_realtime = false; + let mut leave_around = false; + + let timer_timestamp = self.comm.owner().unwrap().get_unit_timestamp(); + let trigger_unit_timestamp = self + .comm + .um() + .get_unit_timestamp(&self.config.unit_ref_target()); + let tts = TripleTimestamp::new().now(); + + let mut v = self.config.value.borrow_mut(); + for i in 0..v.len() { + if v[i].disabled { + continue; + } + + let mut base = 0; + match v[i].timerbase { + TimerBase::Active => { + if self.state().to_unit_active_state() == UnitActiveState::Active { + base = timer_timestamp.borrow().active_exit_timestamp.monotonic; + } else { + base = tts.monotonic; + } + } + TimerBase::Boot | TimerBase::Startup => { + if v[i].timerbase == TimerBase::Boot + && IN_SET!( + Machine::detect_container(), + Machine::Docker, + Machine::Podman, + Machine::Containerd + ) + { + todo!() + } + } + TimerBase::UnitActive => { + leave_around = true; + base = std::cmp::max( + trigger_unit_timestamp + .borrow() + .inactive_exit_timestamp + .monotonic, + self.last_trigger.borrow().monotonic, + ); + if base == 0 { + continue; + } + } + TimerBase::UnitInactive => { + leave_around = true; + base = std::cmp::max( + trigger_unit_timestamp + .borrow() + .inactive_enter_timestamp + .monotonic, + self.last_trigger.borrow().monotonic, + ); + if base == 0 { + continue; + } + } + TimerBase::Calendar => { + todo!() + } + TimerBase::BaseMax | TimerBase::BaseInvalid => {} + } + + if v[i].timerbase != TimerBase::Calendar { + v[i].next_elapse = usec_add( + usec_shift_clock(base, CLOCK_MONOTONIC, self.timer_monotonic_clock()), + v[i].value, + ); + + if duml_timestamp_is_set(*self.last_trigger.borrow()) + && !time_change + && v[i].next_elapse + < triple_timestamp_by_clock(tts, self.timer_monotonic_clock()) + && IN_SET!( + v[i].timerbase, + TimerBase::Active, + TimerBase::Boot, + TimerBase::Startup + ) + { + v[i].disabled = true; + continue; + } + + if !found_monotonic { + *self.next_elapse_monotonic_or_boottime.borrow_mut() = v[i].next_elapse - base; + } else { + *self.next_elapse_monotonic_or_boottime.borrow_mut() = std::cmp::min( + *self.next_elapse_monotonic_or_boottime.borrow(), + v[i].next_elapse, + ) - base; + } + + found_monotonic = true; + } + } + + if !found_monotonic && !found_realtime && !self.on_timezone_change && !self.on_clock_change + { + self.enter_elapsed(leave_around); + return; + } + + if found_monotonic { + let events = self.comm.um().events(); + let source = Rc::clone(&self.mt); + events.del_source(source.clone()).unwrap(); + + self.mt.set_time(usec_add( + *self.next_elapse_monotonic_or_boottime.borrow(), + self.get_randomized_delay_sec( + self.config.config_data().borrow().Timer.RandomizedDelaySec, + ), + )); + events.add_source(source.clone()).unwrap(); + events.set_enabled(source, EventState::OneShot).unwrap(); + } + + if found_realtime { + let events = self.comm.um().events(); + let source = Rc::clone(&self.rt); + events.del_source(source.clone()).unwrap(); + + self.rt.set_time(usec_add( + *self.next_elapse_reltime.borrow(), + self.get_randomized_delay_sec( + self.config.config_data().borrow().Timer.RandomizedDelaySec, + ), + )); + events.add_source(source.clone()).unwrap(); + events.set_enabled(source, EventState::OneShot).unwrap(); + } + + self.set_state(TimerState::Waiting); + } + + pub fn enter_running(&self) { + if let Some(u) = self.comm.owner() { + if self.comm.um().has_stop_job(&u.id()) { + return; + } + + let ret = self + .comm + .um() + .unit_start_by_job(&self.config.unit_ref_target()); + if ret.is_err() { + log::warn!( + "{}: Failed to queue unit startup job!", + &self.config.unit_ref_target() + ); + self.enter_dead(TimerResult::FailureResources); + } + + self.last_trigger.borrow_mut().realtime = now_clockid(CLOCK_REALTIME); + self.last_trigger.borrow_mut().monotonic = now_clockid(CLOCK_MONOTONIC); + + if !self.stamp_path.is_empty() { + touch_file(&self.stamp_path, true, None, None, None).unwrap(); + } + + self.set_state(TimerState::Running); + } else { + self.enter_dead(TimerResult::FailureResources); + } + } + + pub fn enter_elapsed(&self, level_around: bool) { + if level_around || self.config.config_data().borrow().Timer.RemainAfterElapse { + self.set_state(TimerState::Elapsed) + } else { + self.enter_dead(TimerResult::Success) + } + } + + pub fn enter_dead(&self, res: TimerResult) { + log::debug!("timer enter dead state, res {:?}", res); + if self.result() == TimerResult::Success { + self.set_result(res); + } + + let state = if self.result() == TimerResult::Success { + TimerState::Dead + } else { + TimerState::Failed + }; + + self.set_state(state); + } +} + +impl ReStation for TimerMng { + // input: do nothing + + // compensate: do nothing + + // data + fn db_map(&self, _reload: bool) { + let mut lt = self.last_trigger.borrow_mut(); + if let Some((state, result, last_trigger_realtime, last_trigger_monotonic)) = + self.comm.rentry_mng_get() + { + *self.state.borrow_mut() = state; + *self.result.borrow_mut() = result; + lt.realtime = last_trigger_realtime; + lt.monotonic = last_trigger_monotonic; + } + } + + fn db_insert(&self) { + let lt = self.last_trigger.borrow(); + self.comm + .rentry_mng_insert(self.state(), self.result(), lt.realtime, lt.monotonic); + } +} + +impl TimerState { + pub(super) fn to_unit_active_state(self) -> UnitActiveState { + match self { + TimerState::Dead => UnitActiveState::InActive, + TimerState::Waiting | TimerState::Running | TimerState::Elapsed => { + UnitActiveState::Active + } + TimerState::Failed => UnitActiveState::Failed, + TimerState::StateMax | TimerState::StateInvalid => UnitActiveState::DeActivating, + } + } +} + +pub struct MonotonicTimer { + monotonic: RefCell, + mng: RefCell>, +} + +impl MonotonicTimer { + pub fn new(monotonic: u64) -> Self { + MonotonicTimer { + monotonic: RefCell::new(monotonic), + mng: RefCell::new(Weak::new()), + } + } + + pub fn attach_mng(&self, mng: Weak) { + *self.mng.borrow_mut() = mng; + } + + pub(self) fn mng(&self) -> Rc { + self.mng.borrow().clone().upgrade().unwrap() + } + + pub fn set_time(&self, usec: u64) { + *self.monotonic.borrow_mut() = usec; + } + + pub fn do_dispatch(&self) -> i32 { + if self.mng().state() != TimerState::Waiting { + return 0; + } + + log::debug!("Timer elapsed."); + self.mng().enter_running(); + 0 + } +} + +impl Source for MonotonicTimer { + fn token(&self) -> u64 { + let data: u64 = unsafe { std::mem::transmute(self) }; + data + } + + fn priority(&self) -> i8 { + 0i8 + } + + fn event_type(&self) -> EventType { + if self.mng().config.config_data().borrow().Timer.WakeSystem { + EventType::TimerBoottimeAlarm + } else { + EventType::TimerMonotonic + } + } + + fn time_relative(&self) -> u64 { + *self.monotonic.borrow() + } + + fn dispatch(&self, _event: &Events) -> i32 { + self.do_dispatch() + } +} + +pub struct RealtimeTimer { + realtime: RefCell, + mng: RefCell>, +} + +impl RealtimeTimer { + pub fn new(realtime: u64) -> Self { + RealtimeTimer { + realtime: RefCell::new(realtime), + mng: RefCell::new(Weak::new()), + } + } + + pub fn attach_mng(&self, mng: Weak) { + *self.mng.borrow_mut() = mng; + } + + pub(self) fn mng(&self) -> Rc { + self.mng.borrow().clone().upgrade().unwrap() + } + + pub fn set_time(&self, usec: u64) { + *self.realtime.borrow_mut() = usec; + } + + pub fn do_dispatch(&self) -> i32 { + if self.mng().state() != TimerState::Waiting { + return 0; + } + + log::debug!("Timer elapsed."); + self.mng().enter_running(); + 0 + } +} + +impl Source for RealtimeTimer { + fn token(&self) -> u64 { + let data: u64 = unsafe { std::mem::transmute(self) }; + data + } + + fn priority(&self) -> i8 { + 0i8 + } + + fn event_type(&self) -> EventType { + if self.mng().config.config_data().borrow().Timer.WakeSystem { + EventType::TimerRealtimeAlarm + } else { + EventType::TimerRealtime + } + } + + fn time_relative(&self) -> u64 { + *self.realtime.borrow() + } + + fn dispatch(&self, _event: &Events) -> i32 { + self.do_dispatch() + } +} diff --git a/core/coms/timer/src/rentry.rs b/core/coms/timer/src/rentry.rs new file mode 100644 index 0000000000000000000000000000000000000000..51509bd02c42382b9916e47aa3650904bb90e4ce --- /dev/null +++ b/core/coms/timer/src/rentry.rs @@ -0,0 +1,289 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +#![allow(non_snake_case)] +use basic::time_util::{parse_timer, USEC_INFINITY}; +use core::rel::{ReDb, ReDbRwTxn, ReDbTable, ReliSwitch, Reliability}; +use macros::{EnumDisplay, UnitSection}; +use serde::{Deserialize, Serialize}; +use std::rc::Rc; + +struct TimerReDb(ReDb); +const RELI_DB_HTIMER_CONF: &str = "timerconf"; +const RELI_DB_HTIMER_MNG: &str = "timermng"; + +#[derive(PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize, EnumDisplay)] +pub(super) enum TimerState { + Dead, + Waiting, + Running, + Elapsed, + Failed, + StateMax, + StateInvalid, +} + +#[derive(PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize, EnumDisplay)] +pub enum TimerBase { + Active, + Boot, + Startup, + UnitActive, + UnitInactive, + Calendar, + BaseMax, + BaseInvalid, +} + +impl Default for TimerBase { + fn default() -> TimerBase { + TimerBase::BaseMax + } +} + +#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize, Default)] +struct CalendarComponent { + start: i32, + stop: i32, + repeat: i32, +} + +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)] +pub struct CalendarSpec { + weekdays_bits: i32, + end_of_month: bool, + utc: bool, + dst: i32, + timezone: String, + + year: CalendarComponent, + month: CalendarComponent, + day: CalendarComponent, + hour: CalendarComponent, + minute: CalendarComponent, + microsecond: CalendarComponent, +} + +#[derive(UnitSection, Default, Clone, Debug, Serialize, Deserialize)] +#[allow(dead_code)] +pub struct SectionTimer { + #[entry(default = USEC_INFINITY, parser = parse_timer)] + pub AccuracySec: u64, + #[entry(default = USEC_INFINITY, parser = parse_timer)] + pub OnActiveSec: u64, + #[entry(default = USEC_INFINITY, parser = parse_timer)] + pub OnBootSec: u64, + #[entry(default = USEC_INFINITY, parser = parse_timer)] + pub OnStartupSec: u64, + #[entry(default = USEC_INFINITY, parser = parse_timer)] + pub OnUnitActiveSec: u64, + #[entry(default = USEC_INFINITY, parser = parse_timer)] + pub OnUnitInactiveSec: u64, + #[entry(default = USEC_INFINITY, parser = parse_timer)] + pub OnCalendar: u64, + #[entry(default = 0, parser = parse_timer)] + pub RandomizedDelaySec: u64, + pub Unit: Option, + #[entry(default = false)] + pub Persistent: bool, + #[entry(default = false)] + pub WakeSystem: bool, + #[entry(default = true)] + pub RemainAfterElapse: bool, +} + +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)] +pub struct TimerValue { + pub timerbase: TimerBase, + pub disabled: bool, + pub value: u64, + pub calender_spec: CalendarSpec, + pub next_elapse: u64, +} + +impl TimerValue { + pub fn new( + timerbase: TimerBase, + disabled: bool, + value: u64, + calender_spec: CalendarSpec, + next_elapse: u64, + ) -> TimerValue { + TimerValue { + timerbase, + disabled, + value, + calender_spec, + next_elapse, + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] +pub enum TimerResult { + Success, + FailureResources, + FailureStartLimitHit, + ResultMax, + ResultInvalid, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct TimerReConf { + timer: SectionTimer, + unit: String, +} + +impl TimerReConf { + fn new(timer: &SectionTimer, unit: String) -> TimerReConf { + TimerReConf { + timer: timer.clone(), + unit, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct TimerReMng { + state: TimerState, + result: TimerResult, + last_trigger_realtime: u64, + last_trigger_monotonic: u64, +} + +impl TimerReMng { + fn new( + state: TimerState, + result: TimerResult, + last_trigger_realtime: u64, + last_trigger_monotonic: u64, + ) -> TimerReMng { + TimerReMng { + state, + result, + last_trigger_realtime, + last_trigger_monotonic, + } + } +} + +pub(super) struct TimerRe { + // database: multi-instance(N) + conf: Rc>, // RELI_DB_ETIMER_CONF; + mng: Rc>, // RELI_DB_HTIMER_MNG; +} + +impl TimerRe { + pub(super) fn new(relir: &Rc) -> TimerRe { + let conf = Rc::new(TimerReDb(ReDb::new(relir, RELI_DB_HTIMER_CONF))); + let mng = Rc::new(TimerReDb(ReDb::new(relir, RELI_DB_HTIMER_MNG))); + let rentry = TimerRe { conf, mng }; + rentry.register(relir); + rentry + } + + pub(super) fn conf_insert(&self, unit_id: &str, timer: &SectionTimer, unit: String) { + let conf = TimerReConf::new(timer, unit); + self.conf.0.insert(unit_id.to_string(), conf); + } + + pub(super) fn _conf_remove(&self, unit_id: &str) { + self.conf.0.remove(&unit_id.to_string()); + } + + pub(super) fn conf_get(&self, unit_id: &str) -> Option<(SectionTimer, String)> { + let conf = self.conf.0.get(&unit_id.to_string()); + conf.map(|c| (c.timer, c.unit)) + } + + pub(super) fn mng_insert( + &self, + unit_id: &str, + state: TimerState, + result: TimerResult, + last_trigger_realtime: u64, + last_trigger_monotonic: u64, + ) { + let mng = TimerReMng::new(state, result, last_trigger_realtime, last_trigger_monotonic); + self.mng.0.insert(unit_id.to_string(), mng); + } + + pub(super) fn _mng_remove(&self, unit_id: &str) { + self.mng.0.remove(&unit_id.to_string()); + } + + pub(super) fn mng_get(&self, unit_id: &str) -> Option<(TimerState, TimerResult, u64, u64)> { + let mng = self.mng.0.get(&unit_id.to_string()); + mng.map(|m| { + ( + m.state, + m.result, + m.last_trigger_realtime, + m.last_trigger_monotonic, + ) + }) + } + + fn register(&self, relir: &Reliability) { + // rel-db: RELI_DB_HTIMER_CONF + let db = Rc::clone(&self.conf); + relir.history_db_register(RELI_DB_HTIMER_CONF, db); + + // rel-db: RELI_DB_HTIMER_MNG + let db = Rc::clone(&self.mng); + relir.history_db_register(RELI_DB_HTIMER_MNG, db); + } +} + +impl ReDbTable for TimerReDb { + fn clear(&self, wtxn: &mut ReDbRwTxn) { + self.0.do_clear(wtxn); + } + + fn export(&self, db_wtxn: &mut ReDbRwTxn) { + self.0.cache_2_db(db_wtxn); + } + + fn flush(&self, db_wtxn: &mut ReDbRwTxn, switch: ReliSwitch) { + self.0.data_2_db(db_wtxn, switch); + } + + fn import<'a>(&self) { + self.0.db_2_cache(); + } + + fn switch_set(&self, switch: ReliSwitch) { + self.0.switch_buffer(switch); + } +} + +impl ReDbTable for TimerReDb { + fn clear(&self, wtxn: &mut ReDbRwTxn) { + self.0.do_clear(wtxn); + } + + fn export(&self, db_wtxn: &mut ReDbRwTxn) { + self.0.cache_2_db(db_wtxn); + } + + fn flush(&self, db_wtxn: &mut ReDbRwTxn, switch: ReliSwitch) { + self.0.data_2_db(db_wtxn, switch); + } + + fn import<'a>(&self) { + self.0.db_2_cache(); + } + + fn switch_set(&self, switch: ReliSwitch) { + self.0.switch_buffer(switch); + } +} diff --git a/core/coms/timer/src/unit.rs b/core/coms/timer/src/unit.rs new file mode 100644 index 0000000000000000000000000000000000000000..c2b072101ae592cb6c46b79f469673f4a41b1b4f --- /dev/null +++ b/core/coms/timer/src/unit.rs @@ -0,0 +1,157 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use crate::{ + comm::TimerUnitComm, + config::TimerConfig, + load::TimerLoad, + mng::{MonotonicTimer, RealtimeTimer, TimerMng}, +}; +use core::{ + error::*, + rel::{ReStation, Reliability}, + unit::{SubUnit, UnitActiveState, UnitBase, UnitMngUtil}, + UmIf, +}; +use std::{any::Any, path::PathBuf, rc::Rc}; + +struct TimerUnit { + comm: Rc, + config: Rc, + mng: Rc, + load: TimerLoad, +} + +impl ReStation for TimerUnit { + // input: do nothing + + // compensate: do nothing + + // data + fn db_map(&self, reload: bool) { + self.config.db_map(reload); + self.mng.db_map(reload); + } + + fn db_insert(&self) { + self.config.db_insert(); + self.mng.db_insert(); + } +} + +impl SubUnit for TimerUnit { + fn as_any(&self) -> &dyn Any { + self + } + + fn load(&self, paths: Vec) -> Result<()> { + log::debug!("timer begin to load conf file"); + self.config.load(paths, true)?; + + let ret = self.load.timer_add_extras(); + if ret.is_err() { + self.config.reset(); + return ret; + } + + self.verify() + } + + // the function entrance to start the unit + fn start(&self) -> Result<()> { + let starting = self.mng.start()?; + if starting { + log::debug!("timer already in start"); + } + + Ok(()) + } + + fn stop(&self, force: bool) -> Result<()> { + if !force { + let stopping = self.mng.stop()?; + if stopping { + log::debug!("timer already in stop, return immediretly"); + return Ok(()); + } + } + + self.mng.stop().unwrap(); + + Ok(()) + } + + fn reset_failed(&self) { + self.mng.reset_failed() + } + + fn current_active_state(&self) -> UnitActiveState { + self.mng.current_active_state() + } + + fn get_subunit_state(&self) -> String { + self.mng.state().to_string() + } + + fn attach_unit(&self, unit: Rc) { + self.comm.attach_unit(unit); + self.db_insert(); + } + + fn trigger_notify(&self) { + self.mng.trigger_notify() + } +} + +impl UnitMngUtil for TimerUnit { + fn attach_um(&self, um: Rc) { + self.comm.attach_um(um); + } + + fn attach_reli(&self, reli: Rc) { + self.comm.attach_reli(reli); + } +} + +impl TimerUnit { + fn new(_um: Rc) -> TimerUnit { + let comm = Rc::new(TimerUnitComm::new()); + let config = Rc::new(TimerConfig::new(&comm)); + let mt = Rc::new(MonotonicTimer::new(0)); + let rt = Rc::new(RealtimeTimer::new(0)); + let mng = Rc::new(TimerMng::new(&comm, &config, &mt, &rt)); + mt.attach_mng(Rc::downgrade(&mng)); + rt.attach_mng(Rc::downgrade(&mng)); + TimerUnit { + comm: Rc::clone(&comm), + config: Rc::clone(&config), + mng: Rc::clone(&mng), + load: TimerLoad::new(&config, &comm), + } + } + + fn verify(&self) -> Result<()> { + if self.config.value.borrow().is_empty() + && !self.mng.get_on_clock_change() + && !self.mng.get_on_timezone_change() + { + log::error!("Timer unit lacks value setting. Refusing."); + return Err(Error::Nix { + source: nix::Error::ENOEXEC, + }); + } + Ok(()) + } +} + +use core::declare_unitobj_plugin_with_param; +declare_unitobj_plugin_with_param!(TimerUnit, TimerUnit::new); diff --git a/core/libcore/src/unit/base.rs b/core/libcore/src/unit/base.rs index 52224dccfe914a100906d04e7d82d348c7f2a464..d2e25e6f3787cfc5a549c16f552d6b460f18ea45 100644 --- a/core/libcore/src/unit/base.rs +++ b/core/libcore/src/unit/base.rs @@ -16,10 +16,12 @@ use super::state::{UnitActiveState, UnitNotifyFlags}; use super::umif::UnitMngUtil; use super::UnitType; use crate::error::*; +use basic::time_util::UnitTimeStamp; use bitflags::bitflags; use nix::sys::wait::WaitStatus; use nix::{sys::socket::UnixCredentials, unistd::Pid}; use std::any::Any; +use std::cell::RefCell; use std::num::ParseIntError; use std::str::FromStr; use std::{collections::HashMap, path::PathBuf, rc::Rc}; @@ -70,6 +72,9 @@ pub trait UnitBase { /// guess main pid from the cgroup path fn guess_main_pid(&self) -> Result; + + /// + fn get_unit_timestamp(&self) -> Rc>; } ///The trait Defining Shared Behavior of sub unit @@ -178,6 +183,9 @@ pub trait SubUnit: ReStation + UnitMngUtil { /// fn update_mount_state_by_mountinfo(&self) {} + + /// + fn trigger_notify(&self) {} } /// the macro for create a sub unit instance with dyn ref of UmIf, diff --git a/core/libcore/src/unit/deps.rs b/core/libcore/src/unit/deps.rs index 3b2c3a97a696eb72db0144ade876659ad0e90bce..4eeedc8ad5133c5725a3208c96a92808eca3f5c1 100644 --- a/core/libcore/src/unit/deps.rs +++ b/core/libcore/src/unit/deps.rs @@ -114,6 +114,7 @@ pub enum UnitType { UnitTarget, UnitSocket, UnitMount, + UnitTimer, UnitTypeMax, UnitTypeInvalid, UnitTypeErrnoMax, @@ -127,6 +128,7 @@ impl UnitType { UnitType::UnitTarget, UnitType::UnitSocket, UnitType::UnitMount, + UnitType::UnitTimer, ] .iter() .copied() @@ -142,6 +144,7 @@ impl FromStr for UnitType { "target" => UnitType::UnitTarget, "socket" => UnitType::UnitSocket, "mount" => UnitType::UnitMount, + "timer" => UnitType::UnitTimer, _ => UnitType::UnitTypeInvalid, }; Ok(ret) @@ -155,6 +158,7 @@ impl From for String { UnitType::UnitTarget => "target".into(), UnitType::UnitSocket => "socket".into(), UnitType::UnitMount => "mount".into(), + UnitType::UnitTimer => "timer".into(), UnitType::UnitTypeMax => null_str!(""), UnitType::UnitTypeInvalid => null_str!(""), UnitType::UnitTypeErrnoMax => null_str!(""), @@ -170,6 +174,7 @@ impl TryFrom for UnitType { 1 => Ok(UnitType::UnitTarget), 2 => Ok(UnitType::UnitSocket), 3 => Ok(UnitType::UnitMount), + 4 => Ok(UnitType::UnitTimer), v => Err(format!("input {} is invalid", v)), } } diff --git a/core/libcore/src/unit/umif.rs b/core/libcore/src/unit/umif.rs index 205055d45b0ccac0a94f744600cb99ba32a02caa..02efad2d4ae0f134d63e3e4adeccea1823520097 100644 --- a/core/libcore/src/unit/umif.rs +++ b/core/libcore/src/unit/umif.rs @@ -16,8 +16,10 @@ use crate::error::*; use crate::exec::{ExecCommand, ExecContext, ExecParameters}; use crate::rel::{ReStation, Reliability}; use crate::unit::{UnitActiveState, UnitType}; +use basic::time_util::UnitTimeStamp; use event::Events; use nix::unistd::Pid; +use std::cell::RefCell; use std::{path::PathBuf, rc::Rc}; #[cfg(feature = "plugin")] @@ -231,6 +233,13 @@ pub trait UmIf { /// update mount state fn update_mount_state_by_mountinfo(&self, _unit_name: &str) {} + + /// get unit timestamp data + fn get_unit_timestamp(&self, _unit_name: &str) -> Rc> { + Rc::new(RefCell::new(UnitTimeStamp::default())) + } + /// unit trigger TriggerBy units + fn trigger_notify(&self, _name: &str) {} } /// the trait used for attach UnitManager to sub unit diff --git a/core/sysmaster/Cargo.toml b/core/sysmaster/Cargo.toml index 30d7d95b902c4927b46440e8c1a705997721cce6..790510fd000d70654c011b79c025cb2562bc47e9 100644 --- a/core/sysmaster/Cargo.toml +++ b/core/sysmaster/Cargo.toml @@ -136,8 +136,13 @@ path = "../coms/mount" optional = true default-features = false +[dependencies.timer] +path = "../coms/timer" +optional = true +default-features = false + [features] -default = ["linux", "noplugin", "mount", "socket", "service", "target"] +default = ["linux", "noplugin", "mount", "socket", "service", "target", "timer"] hongmeng = [] linux = [] noplugin = [] diff --git a/core/sysmaster/src/manager/rentry.rs b/core/sysmaster/src/manager/rentry.rs index d098192482bf2a432121012b2c01d9968e9d31f9..610cecfb9b2b00da1e2af52881b63d49fb226c8a 100644 --- a/core/sysmaster/src/manager/rentry.rs +++ b/core/sysmaster/src/manager/rentry.rs @@ -82,7 +82,11 @@ const RELI_DB_HMOUNTM_FRAME: &str = "mntm-frame"; /* target */ const RELI_DB_HTARGET_MNG: &str = "tarmng"; -pub const RELI_HISTORY_MAX_DBS: u32 = 18; +/* timer */ +const RELI_DB_HTIMER_CONF: &str = "timerconf"; +const RELI_DB_HTIMER_MNG: &str = "timermng"; + +pub const RELI_HISTORY_MAX_DBS: u32 = 20; #[allow(dead_code)] static RELI_HISTORY_DB_NAME: [&str; RELI_HISTORY_MAX_DBS as usize] = [ RELI_DB_HJOB_TRIGGER, @@ -103,4 +107,6 @@ static RELI_HISTORY_DB_NAME: [&str; RELI_HISTORY_MAX_DBS as usize] = [ RELI_DB_HMOUNT_MNG, RELI_DB_HMOUNTM_FRAME, RELI_DB_HTARGET_MNG, + RELI_DB_HTIMER_CONF, + RELI_DB_HTIMER_MNG, ]; diff --git a/core/sysmaster/src/unit/entry/uentry.rs b/core/sysmaster/src/unit/entry/uentry.rs index f6ba2e0a64c980986f91df162a2d0d85a643a82e..ca995b15576262ba9b6d0f4b72eeb904fc156b68 100644 --- a/core/sysmaster/src/unit/entry/uentry.rs +++ b/core/sysmaster/src/unit/entry/uentry.rs @@ -22,11 +22,13 @@ use crate::unit::data::{DataManager, UnitState}; use crate::unit::rentry::{UnitLoadState, UnitRe}; use crate::unit::util::UnitFile; use basic::process::{self, my_child}; +use basic::time_util::{now_clockid, UnitTimeStamp}; use cgroup::{self, CgFlags}; use core::error::*; use core::rel::ReStation; use core::unit::{KillContext, KillMode, KillOperation, UnitNotifyFlags}; use core::unit::{SubUnit, UnitActiveState, UnitBase, UnitType}; +use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; use nix::sys::socket::UnixCredentials; use nix::sys::wait::WaitStatus; use nix::unistd::Pid; @@ -55,6 +57,7 @@ pub struct Unit { sub: Box, merged_into: RefCell>>, in_stop_when_bound_queue: RefCell, + timestamp: Rc>, } impl PartialEq for Unit { @@ -185,6 +188,10 @@ impl UnitBase for Unit { fn guess_main_pid(&self) -> Result { self.guess_main_pid() } + + fn get_unit_timestamp(&self) -> Rc> { + self.get_unit_timestamp() + } } impl Unit { @@ -211,6 +218,7 @@ impl Unit { start_limit: StartLimit::new(), merged_into: RefCell::new(None), in_stop_when_bound_queue: RefCell::new(false), + timestamp: Rc::new(RefCell::new(UnitTimeStamp::default())), }); let owner = Rc::clone(&_u); _u.sub.attach_unit(owner); @@ -298,6 +306,10 @@ impl Unit { Rc::clone(&self.conditions) } + pub fn unit_trigger_notify(&self) { + self.sub.trigger_notify() + } + /// pub fn notify( &self, @@ -313,6 +325,23 @@ impl Unit { ); } + let mut unit_timestamp = self.timestamp.borrow_mut(); + + unit_timestamp.state_change_timestamp.realtime = now_clockid(CLOCK_REALTIME); + unit_timestamp.state_change_timestamp.monotonic = now_clockid(CLOCK_MONOTONIC); + + if original_state.is_inactive_or_failed() && !new_state.is_inactive_or_failed() { + unit_timestamp.inactive_exit_timestamp = unit_timestamp.state_change_timestamp; + } else if !original_state.is_inactive_or_failed() && new_state.is_inactive_or_failed() { + unit_timestamp.inactive_enter_timestamp = unit_timestamp.state_change_timestamp; + } + + if !original_state.is_active_or_reloading() && new_state.is_active_or_reloading() { + unit_timestamp.active_enter_timestamp = unit_timestamp.state_change_timestamp; + } else if original_state.is_active_or_reloading() && !new_state.is_active_or_reloading() { + unit_timestamp.active_exit_timestamp = unit_timestamp.state_change_timestamp; + } + let u_state = UnitState::new(original_state, new_state, flags); self.dm.insert_unit_state(self.id(), u_state); } @@ -820,6 +849,10 @@ impl Unit { ) -> Result<()> { self.sub.notify_message(ucred, messages, fds) } + + pub fn get_unit_timestamp(&self) -> Rc> { + Rc::clone(&self.timestamp) + } } #[cfg(test)] diff --git a/core/sysmaster/src/unit/manager.rs b/core/sysmaster/src/unit/manager.rs index 3941d46f4d8cf6682192edf884d0226c50a8e03e..7eedc2f28180789f6b63326c4e5679d685cca7fd 100644 --- a/core/sysmaster/src/unit/manager.rs +++ b/core/sysmaster/src/unit/manager.rs @@ -37,6 +37,7 @@ use crate::utils::table::{TableOp, TableSubscribe}; use basic::cmdline::get_process_cmdline; use basic::fs_util::LookupPaths; use basic::show_table::{CellColor, ShowTable}; +use basic::time_util::UnitTimeStamp; use basic::{machine, process, rlimit, signal_util}; use constants::SIG_SWITCH_ROOT_OFFSET; use core::error::*; @@ -555,6 +556,16 @@ impl UmIf for UnitManager { .collect::>() } + fn get_unit_timestamp(&self, unit_name: &str) -> Rc> { + let s_unit = if let Some(unit) = self.db.units_get(unit_name) { + unit + } else { + log::error!("unit [{}] not found!!!!!", unit_name); + return Rc::new(RefCell::new(UnitTimeStamp::default())); + }; + s_unit.unit().get_unit_timestamp() + } + fn unit_has_default_dependecy(&self, _unit_name: &str) -> bool { let s_unit = if let Some(s_unit) = self.db.units_get(_unit_name) { s_unit @@ -712,6 +723,13 @@ impl UmIf for UnitManager { }; mount.update_mount_state_by_mountinfo(); } + + fn trigger_notify(&self, name: &str) { + let deps = self.db.dep_gets(name, UnitRelations::UnitTriggeredBy); + for dep in deps.iter() { + dep.unit_trigger_notify() + } + } } /// the declaration "pub(self)" is for identification only. diff --git a/core/sysmaster/src/unit/util/unit_om.rs b/core/sysmaster/src/unit/util/unit_om.rs index 41b0339e93f8b3fe06830177e848f29cb7d9d4a9..1373d5f4ad0bf1b80f6bcf83e88f51cb6791fd8a 100644 --- a/core/sysmaster/src/unit/util/unit_om.rs +++ b/core/sysmaster/src/unit/util/unit_om.rs @@ -66,6 +66,8 @@ mod noplugin { use std::rc::Rc; #[cfg(feature = "target")] use target::{self}; + #[cfg(feature = "timer")] + use timer::{self}; pub(super) fn create_um_obj( unit_type: UnitType, @@ -82,6 +84,8 @@ mod noplugin { UnitType::UnitSocket => socket::__um_obj_create, #[cfg(feature = "target")] UnitType::UnitTarget => target::__um_obj_create, + #[cfg(feature = "timer")] + UnitType::UnitTimer => timer::__um_obj_create, _ => { return Err(Error::Other { msg: "Component unsupported!".to_string(), @@ -108,6 +112,8 @@ mod noplugin { UnitType::UnitSocket => socket::__subunit_create_with_params, #[cfg(feature = "target")] UnitType::UnitTarget => target::__subunit_create_with_params, + #[cfg(feature = "timer")] + UnitType::UnitTimer => timer::__subunit_create_with_params, _ => { return Err(Error::Other { msg: "Component unsupported!".to_string(), diff --git a/libs/basic/Cargo.toml b/libs/basic/Cargo.toml index aced5a4f62d9fa434f61782221918f644779fe98..53c096ac2329e5c783ea71be44fd35a04bc3f7ce 100644 --- a/libs/basic/Cargo.toml +++ b/libs/basic/Cargo.toml @@ -21,6 +21,7 @@ procfs = { version = "0.12.0", default-features = false, optional = true } rand = { version = "0.8.5", optional = true } snafu = { version = "0.7", features = ["std"], default-features = false } fnmatch-sys = "1.0.0" +chrono = "0.4.26" [dev-dependencies] tempfile = "3.6.0" diff --git a/libs/basic/src/lib.rs b/libs/basic/src/lib.rs index 5007047c0754c1ae3a9a3fcde08f1b52f42fca05..544d524d0aea8a2e38eb520ce8b3a4c425a50003 100644 --- a/libs/basic/src/lib.rs +++ b/libs/basic/src/lib.rs @@ -102,6 +102,9 @@ pub const SHUTDOWN_TARGET: &str = "shutdown.target"; /// the socketc target pub const SOCKETS_TARGET: &str = "sockets.target"; +/// the timer target +pub const TIMERS_TARGET: &str = "timers.target"; + /// early boot targets pub const SYSINIT_TARGET: &str = "sysinit.target"; /// the basic start target diff --git a/libs/basic/src/time_util.rs b/libs/basic/src/time_util.rs index 39bed5f4e2e758a7fe7b2589c19e1fad7903f41a..3c9aaa5c79a57f82a81d9475241eb126cf0b2754 100644 --- a/libs/basic/src/time_util.rs +++ b/libs/basic/src/time_util.rs @@ -11,15 +11,24 @@ // See the Mulan PSL v2 for more details. //!Parse time - +#![allow(missing_docs)] +use chrono::DateTime; use libc::{c_char, strtoll}; +use libc::{ + clockid_t, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM, CLOCK_MONOTONIC, CLOCK_REALTIME, + CLOCK_REALTIME_ALARM, +}; use nix::errno::Errno; use std::ffi::CStr; use std::ffi::CString; +use std::mem; /// USec infinity pub const USEC_INFINITY: u64 = u64::MAX; +/// NSec infinity +pub const NSEC_INFINITY: u64 = u64::MAX; + /// USec per Sec pub const USEC_PER_SEC: u64 = 1000000; /// USec per MSec @@ -162,6 +171,17 @@ pub fn parse_sec(t: &str) -> Result { parse_time(t, USEC_PER_SEC) } +///parse time string to sec, include calendar string +pub fn parse_timer(date: &str) -> Result { + let formats = ["%Y-%m-&d", "%Y-%m-%d %H:%M:%M:%S"]; + for format in formats { + if let Ok(dt) = DateTime::parse_from_str(date, format) { + return Ok(dt.timestamp_micros() as u64); + } + } + parse_time(date, USEC_PER_SEC) +} + struct Table<'a> { suffix: &'a str, usec: u64, @@ -293,6 +313,192 @@ fn extract_multiplier(p: &mut &str, multiplier: &mut u64) { } } +#[derive(Default, Clone, Copy, Debug)] +pub struct DualTimestamp { + pub realtime: u64, + pub monotonic: u64, +} + +#[derive(Clone, Copy, Default)] +pub struct TripleTimestamp { + pub realtime: u64, + pub monotonic: u64, + pub boottime: u64, +} + +impl TripleTimestamp { + pub fn new() -> TripleTimestamp { + Self { + realtime: 0, + monotonic: 0, + boottime: 0, + } + } + + pub fn now(&mut self) -> Self { + unsafe { + let mut tp = mem::MaybeUninit::zeroed().assume_init(); + libc::clock_gettime(libc::CLOCK_REALTIME, &mut tp); + self.realtime = timespec_load(tp); + libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut tp); + self.monotonic = timespec_load(tp); + libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut tp); + self.boottime = timespec_load(tp); + } + *self + } +} + +pub fn timespec_load(ts: libc::timespec) -> u64 { + if ts.tv_sec < 0 || ts.tv_nsec < 0 { + return USEC_INFINITY; + } + + if (ts.tv_sec as u64) > (USEC_INFINITY - ((ts.tv_nsec as u64) / NSEC_PER_SEC) / USEC_PER_SEC) { + return USEC_INFINITY; + } + + (ts.tv_sec as u64) * USEC_PER_SEC + (ts.tv_nsec as u64) / NSEC_PER_USEC +} + +pub fn timespec_load_nsec(ts: libc::timespec) -> u64 { + if ts.tv_sec < 0 || ts.tv_nsec < 0 { + return NSEC_INFINITY; + } + + if (ts.tv_sec as u64) >= (NSEC_INFINITY - ((ts.tv_nsec as u64) / NSEC_PER_SEC)) { + return NSEC_INFINITY; + } + + (ts.tv_sec as u64) * NSEC_PER_SEC + (ts.tv_nsec as u64) +} + +pub fn timestamp_is_set(timestamp: u64) -> bool { + timestamp > 0 && timestamp != USEC_INFINITY +} + +pub fn duml_timestamp_is_set(dt: DualTimestamp) -> bool { + timestamp_is_set(dt.realtime) || timestamp_is_set(dt.monotonic) +} + +pub fn usec_add(a: u64, b: u64) -> u64 { + if a > USEC_INFINITY - b { + return USEC_INFINITY; + } + + a + b +} + +pub fn usec_sub_unsigned(a: u64, b: u64) -> u64 { + if a == USEC_INFINITY { + return USEC_INFINITY; + } + + if a < b { + return 0; + } + + a - b +} + +pub fn usec_sub_signed(a: u64, b: i64) -> u64 { + if b == i64::MIN { + return usec_add(a, i64::MAX as u64 + 1); + } + + if b < 0 { + usec_add(a, -b as u64); + } + + usec_sub_unsigned(a, b as u64) +} + +pub fn map_clock_id(c: clockid_t) -> clockid_t { + match c { + CLOCK_BOOTTIME_ALARM => CLOCK_BOOTTIME, + CLOCK_REALTIME_ALARM => CLOCK_REALTIME, + _ => c, + } +} + +pub fn now_clockid(c: clockid_t) -> u64 { + let now = TripleTimestamp::new().now(); + match c { + CLOCK_BOOTTIME | CLOCK_BOOTTIME_ALARM => now.boottime, + CLOCK_REALTIME | CLOCK_REALTIME_ALARM => now.realtime, + _ => now.monotonic, + } +} + +pub fn usec_shift_clock(x: u64, from: clockid_t, to: clockid_t) -> u64 { + if x == USEC_INFINITY { + return USEC_INFINITY; + } + + if map_clock_id(from) == map_clock_id(to) { + return x; + } + + let a = now_clockid(from); + let b = now_clockid(to); + + if x > a { + usec_add(b, usec_sub_unsigned(x, a)) + } else { + usec_sub_unsigned(b, usec_sub_unsigned(a, x)) + } +} + +#[allow(unused_variables)] +pub fn triple_timestamp_by_clock(ts: TripleTimestamp, clock: clockid_t) -> u64 { + match clock { + CLOCK_BOOTTIME | CLOCK_BOOTTIME_ALARM => ts.boottime, + CLOCK_MONOTONIC => ts.monotonic, + CLOCK_REALTIME | CLOCK_REALTIME_ALARM => ts.realtime, + _ => USEC_INFINITY, + } +} + +#[derive(Default, Clone, Copy, Debug)] +pub struct UnitTimeStamp { + pub inactive_exit_timestamp: DualTimestamp, + pub active_enter_timestamp: DualTimestamp, + pub active_exit_timestamp: DualTimestamp, + pub inactive_enter_timestamp: DualTimestamp, + pub state_change_timestamp: DualTimestamp, +} + +fn map_clock_usec_internal(from: u64, from_base: u64, to_base: u64) -> u64 { + if from >= from_base { + let delta = from - from_base; + + if to_base >= USEC_INFINITY - delta { + return USEC_INFINITY; + } + + to_base + delta + } else { + let delta = from_base - from; + if to_base <= delta { + return 0; + } + + to_base - delta + } +} + +pub fn map_clock_usec(from: u64, from_clock: clockid_t, to_clock: clockid_t) -> u64 { + if map_clock_id(from_clock) == map_clock_id(to_clock) { + return from; + } + + if from == USEC_INFINITY { + return from; + } + + map_clock_usec_internal(from, now_clockid(from_clock), now_clockid(to_clock)) +} + #[cfg(test)] mod tests { use super::*;