From 46d327591a28a91f7ab05231286b83d27adee739 Mon Sep 17 00:00:00 2001 From: huyubiao Date: Sat, 18 Nov 2023 00:09:00 +0800 Subject: [PATCH] feature(devmaster): Add subcommands for devctl settle --- Cargo.lock | 2 +- core/coms/service/Cargo.toml | 1 + core/coms/service/src/config.rs | 2 +- core/coms/service/src/rentry.rs | 2 +- exts/devmaster/Cargo.toml | 1 + exts/devmaster/src/bin/devctl/main.rs | 28 +- .../src/bin/devctl/subcmds/devctl_settle.rs | 260 +++++++++++ .../src/bin/devctl/subcmds/devctl_utils.rs | 14 + exts/devmaster/src/bin/devctl/subcmds/mod.rs | 1 + .../src/lib/framework/control_manager.rs | 3 + libs/basic/Cargo.toml | 2 + libs/basic/src/lib.rs | 2 + libs/basic/src/time_util.rs | 402 ++++++++++++++++++ libs/constants/src/lib.rs | 9 - libs/event/Cargo.toml | 2 +- libs/event/src/timer.rs | 2 +- 16 files changed, 717 insertions(+), 16 deletions(-) create mode 100644 exts/devmaster/src/bin/devctl/subcmds/devctl_settle.rs create mode 100644 libs/basic/src/time_util.rs diff --git a/Cargo.lock b/Cargo.lock index a73524a2..1e22becd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,7 +490,7 @@ dependencies = [ name = "event" version = "0.5.1" dependencies = [ - "constants", + "basic", "libc", "libtests", "nix 0.24.3", diff --git a/core/coms/service/Cargo.toml b/core/coms/service/Cargo.toml index f60b58c4..408fa075 100644 --- a/core/coms/service/Cargo.toml +++ b/core/coms/service/Cargo.toml @@ -15,6 +15,7 @@ basic = { path = "../../../libs/basic", default-features = false, features = [ "fs", "fd", "unit_name", + "time_util", ] } cgroup = { path = "../../../libs/cgroup", default-features = false } constants = { path = "../../../libs/constants" } diff --git a/core/coms/service/src/config.rs b/core/coms/service/src/config.rs index e69ba089..ab0f38f1 100644 --- a/core/coms/service/src/config.rs +++ b/core/coms/service/src/config.rs @@ -13,8 +13,8 @@ #![allow(non_snake_case)] use super::comm::ServiceUnitComm; use super::rentry::{NotifyAccess, SectionService, ServiceCommand, ServiceType}; +use basic::time_util::{USEC_INFINITY, USEC_PER_SEC}; use basic::unit_name::unit_name_to_instance; -use constants::{USEC_INFINITY, USEC_PER_SEC}; use core::error::*; use core::exec::ExecCommand; use core::rel::ReStation; diff --git a/core/coms/service/src/rentry.rs b/core/coms/service/src/rentry.rs index 914d122c..8195cdc5 100644 --- a/core/coms/service/src/rentry.rs +++ b/core/coms/service/src/rentry.rs @@ -33,8 +33,8 @@ use core::exec::{ExecCommand, Rlimit, RuntimeDirectory, StateDirectory, WorkingD use core::rel::{ReDb, ReDbRwTxn, ReDbTable, ReliSwitch, Reliability}; use core::unit::KillMode; +use basic::time_util::USEC_PER_SEC; use basic::EXEC_RUNTIME_PREFIX; -use constants::USEC_PER_SEC; struct ServiceReDb(ReDb); diff --git a/exts/devmaster/Cargo.toml b/exts/devmaster/Cargo.toml index c52e9880..4a3a8176 100644 --- a/exts/devmaster/Cargo.toml +++ b/exts/devmaster/Cargo.toml @@ -32,6 +32,7 @@ basic = { path = "../../libs/basic", default-features = false, features = [ "socket", "io", "argv", + "time_util", ] } blkid_rs = { path = "../../libs/blkid_rs" } device = { path = "../../libs/device" } diff --git a/exts/devmaster/src/bin/devctl/main.rs b/exts/devmaster/src/bin/devctl/main.rs index 8f72285e..1340a467 100644 --- a/exts/devmaster/src/bin/devctl/main.rs +++ b/exts/devmaster/src/bin/devctl/main.rs @@ -26,6 +26,7 @@ use std::{io::Write, os::unix::net::UnixStream}; use subcmds::devctl_hwdb::subcommand_hwdb; use subcmds::devctl_info::InfoArgs; use subcmds::devctl_monitor::subcommand_monitor; +use subcmds::devctl_settle::SettleArgs; use subcmds::devctl_test_builtin::subcommand_test_builtin; use subcmds::devctl_trigger::TriggerArgs; use subcmds::Result; @@ -170,8 +171,24 @@ enum SubCmd { devices: Vec, }, - /// Test builtin command on a device + /// Kill all devmaster workers #[clap(display_order = 5)] + Settle { + /// Maximum time to wait for events + #[clap(short('t'), long)] + timeout: Option, + + /// Stop waiting if file exists + #[clap(short('E'), long)] + exit_if_exists: Option, + + /// other args + #[clap(required = false)] + other: Vec, + }, + + /// Test builtin command on a device + #[clap(display_order = 6)] TestBuiltin { /// device action #[clap(short, long)] @@ -184,7 +201,7 @@ enum SubCmd { syspath: String, }, /// Test builtin command on a device - #[clap(display_order = 6)] + #[clap(display_order = 7)] Hwdb { /// update the hardware database #[clap(short('u'), long)] @@ -303,6 +320,13 @@ fn main() -> Result<()> { ) .subcommand() } + SubCmd::Settle { + timeout, + exit_if_exists, + other, + } => { + return SettleArgs::new(timeout, exit_if_exists, other).subcommand(); + } SubCmd::TestBuiltin { action, builtin, diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_settle.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_settle.rs new file mode 100644 index 00000000..a276099d --- /dev/null +++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_settle.rs @@ -0,0 +1,260 @@ +// 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. + +//! subcommand for devctl settle +//! + +use crate::subcmds::devctl_utils::devmaster_queue_is_empty; +use crate::Result; +use basic::time_util::{parse_sec, USEC_INFINITY, USEC_PER_SEC}; +use event::{EventState, EventType, Events, Source}; +use libdevmaster::framework::control_manager::CONTROL_MANAGER_LISTEN_ADDR; +use nix::sys::inotify::AddWatchFlags; +use nix::unistd::{access, getuid, AccessFlags}; +use std::io::Write; +use std::os::unix::io::RawFd; +use std::os::unix::net::UnixStream; +use std::rc::Rc; + +#[derive(Debug)] +pub struct SettleArgs { + timeout: Option, + exit_if_exists: Option, + _other: Vec, +} + +impl SettleArgs { + pub fn new( + timeout: Option, + exit_if_exists: Option, + _other: Vec, + ) -> Self { + SettleArgs { + timeout, + exit_if_exists, + _other, + } + } + + /// subcommand for Wait for pending uevents + pub fn subcommand(&self) -> Result<()> { + let timeout = match &self.timeout { + Some(timeout) => match parse_sec(timeout) { + Ok(d) => d, + Err(err) => { + log::error!("Failed to parse timeout value:{:?} err:{:?}", timeout, err); + return Err(err); + } + }, + None => 120 * USEC_PER_SEC, + }; + + // TODO: emit_deprecation_warning(); dbus is not currently implemented + + if 0 == getuid().as_raw() { + /* guarantee that the devmaster daemon isn't pre-processing */ + let mut stream = match UnixStream::connect(CONTROL_MANAGER_LISTEN_ADDR) { + Ok(stream) => stream, + Err(err) => { + log::error!( + "Failed to connect to devmaster daemon, ignoring err:{:?}", + err + ); + return Ok(()); + } + }; + if let Err(err) = stream.write_all(b"ping ") { + log::error!("Failed to ping to devmaster daemon, err:{:?}", err); + return Ok(()); + } + } else { + /* For non-privileged users, at least check if devmaster is running. */ + if let Err(err) = access(CONTROL_MANAGER_LISTEN_ADDR, AccessFlags::F_OK) { + if err == nix::Error::ENOENT { + log::error!("devmaster is not running err:{:?}", err); + } else { + log::error!( + "Failed to check if {} exists err:{:?}", + CONTROL_MANAGER_LISTEN_ADDR, + err + ); + } + return Err(err); + } + } + + let events = Events::new().unwrap(); + if timeout != USEC_INFINITY { + let time: Rc = Rc::new(Timer::new(timeout)); + events.add_source(time.clone()).unwrap(); + events + .set_enabled(time.clone(), EventState::OneShot) + .unwrap(); + } + + let _wd = events.add_watch("/run/devmaster", AddWatchFlags::IN_DELETE); + let s: Rc = Rc::new(Inotify::new(self.exit_if_exists.clone())); + + events.add_source(s.clone()).unwrap(); + events.set_enabled(s.clone(), EventState::On).unwrap(); + + /* Check before entering the event loop, as the devmaster queue may be already empty. */ + if check(&self.exit_if_exists) { + return Ok(()); + } + + events.rloop().unwrap(); + Ok(()) + } +} + +/// trigger monitor +#[derive(Debug)] +struct Inotify { + exit_if_exists: Option, +} + +/// public methods +impl Inotify { + /// create a monitor instance for monitoring trigger + pub fn new(exit_if_exists: Option) -> Self { + Inotify { exit_if_exists } + } +} + +impl Source for Inotify { + /// socket fd + fn fd(&self) -> RawFd { + 0 + } + + /// event type + fn event_type(&self) -> EventType { + EventType::Inotify + } + + /// epoll type + fn epoll_event(&self) -> u32 { + (libc::EPOLLIN) as u32 + } + + /// priority of event source + fn priority(&self) -> i8 { + 0i8 + } + + fn time_relative(&self) -> u64 { + 0 + } + + /// receive device from socket and remove path or uuid from settle_path_or_ids + fn dispatch(&self, event: &Events) -> i32 { + let _events = event.read_events(); + + if check(&self.exit_if_exists) { + event.set_exit(); + } + + 0 + } + + /// token of event source + fn token(&self) -> u64 { + let data: u64 = unsafe { std::mem::transmute(self) }; + data + } +} + +/// trigger monitor +#[derive(Debug)] +struct Timer { + time_usec: u64, +} + +/// public methods +impl Timer { + /// create a monitor instance for monitoring trigger + pub fn new(time_usec: u64) -> Self { + Timer { time_usec } + } +} + +impl Source for Timer { + /// socket fd + fn fd(&self) -> RawFd { + 0 + } + + /// event type + fn event_type(&self) -> EventType { + EventType::TimerBoottime + } + + /// epoll type + fn epoll_event(&self) -> u32 { + (libc::EPOLLIN) as u32 + } + + /// priority of event source + fn priority(&self) -> i8 { + 0i8 + } + + fn time_relative(&self) -> u64 { + self.time_usec + } + + /// receive device from socket and remove path or uuid from settle_path_or_ids + fn dispatch(&self, event: &Events) -> i32 { + event.set_exit(); + 0 + } + + /// token of event source + fn token(&self) -> u64 { + let data: u64 = unsafe { std::mem::transmute(self) }; + data + } +} + +/// check the existence of exit_if_exists +fn check(exit_if_exists: &Option) -> bool { + if let Some(path) = exit_if_exists { + match access(path.as_str(), AccessFlags::F_OK) { + Ok(()) => return true, + Err(err) => { + if err != nix::Error::ENOENT { + log::error!( + "Failed to check the existence of {:?}, ignoring: {:?}", + path, + err + ); + } + } + } + } + + /* exit if queue is empty */ + match devmaster_queue_is_empty() { + Ok(flag) => flag, + Err(err) => { + if err != nix::Error::ENOENT { + log::error!( + "Failed to check if devmaster queue is empty, ignoring: {:?}", + err + ); + } + + false + } + } +} diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs index ecaab3a2..8eda8d23 100644 --- a/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs +++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_utils.rs @@ -1,4 +1,5 @@ use device::Device; +use nix::unistd::{access, AccessFlags}; use std::path::PathBuf; type Result = std::result::Result; @@ -36,3 +37,16 @@ pub fn find_device(id: &str, prefix: &str) -> Result { fn find_device_from_unit(_unit_name: &str) -> Result { todo!() } + +/// check if the queue is empty +pub fn devmaster_queue_is_empty() -> Result { + match access("/run/devmaster/queue", AccessFlags::F_OK) { + Ok(()) => Ok(false), + Err(err) => { + if err == nix::Error::ENOENT { + return Ok(true); + } + Err(err) + } + } +} diff --git a/exts/devmaster/src/bin/devctl/subcmds/mod.rs b/exts/devmaster/src/bin/devctl/subcmds/mod.rs index e4289028..25936c6d 100644 --- a/exts/devmaster/src/bin/devctl/subcmds/mod.rs +++ b/exts/devmaster/src/bin/devctl/subcmds/mod.rs @@ -16,6 +16,7 @@ pub(crate) mod devctl_hwdb; pub(crate) mod devctl_info; pub(crate) mod devctl_monitor; +pub(crate) mod devctl_settle; pub(crate) mod devctl_test_builtin; pub(crate) mod devctl_trigger; pub(self) mod devctl_utils; diff --git a/exts/devmaster/src/lib/framework/control_manager.rs b/exts/devmaster/src/lib/framework/control_manager.rs index d5a5f440..28d6c910 100644 --- a/exts/devmaster/src/lib/framework/control_manager.rs +++ b/exts/devmaster/src/lib/framework/control_manager.rs @@ -88,6 +88,9 @@ impl ControlManager { "exit" => { self.events.set_exit(); } + "ping" => { + log::debug!("Received devmaster control message (PING)"); + } _ => { todo!(); } diff --git a/libs/basic/Cargo.toml b/libs/basic/Cargo.toml index bf0c4d46..fd1979ea 100644 --- a/libs/basic/Cargo.toml +++ b/libs/basic/Cargo.toml @@ -66,6 +66,7 @@ full = [ "random", "id128", "namespace", + "time_util", ] capability = [] @@ -117,3 +118,4 @@ sysfs = ["nix/dir"] unistd = ["nix/user"] unit_name = [] uuid = ["bitflags", "random"] +time_util = [] diff --git a/libs/basic/src/lib.rs b/libs/basic/src/lib.rs index 41877150..3aabd021 100644 --- a/libs/basic/src/lib.rs +++ b/libs/basic/src/lib.rs @@ -81,6 +81,8 @@ pub mod strbuf; pub mod string; #[cfg(feature = "sysfs")] pub mod sysfs; +#[cfg(feature = "time_util")] +pub mod time_util; #[cfg(feature = "unistd")] pub mod unistd; #[cfg(feature = "unit_name")] diff --git a/libs/basic/src/time_util.rs b/libs/basic/src/time_util.rs new file mode 100644 index 00000000..39bed5f4 --- /dev/null +++ b/libs/basic/src/time_util.rs @@ -0,0 +1,402 @@ +// 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. + +//!Parse time + +use libc::{c_char, strtoll}; +use nix::errno::Errno; +use std::ffi::CStr; +use std::ffi::CString; + +/// USec infinity +pub const USEC_INFINITY: u64 = u64::MAX; + +/// USec per Sec +pub const USEC_PER_SEC: u64 = 1000000; +/// USec per MSec +pub const USEC_PER_MSEC: u64 = 1000; +/// NSec per Sec +pub const NSEC_PER_SEC: u64 = 1000000000; +/// NSec per USec +pub const NSEC_PER_USEC: u64 = 1000; + +/// USec per Minute +pub const USEC_PER_MINUTE: u64 = 60 * USEC_PER_SEC; +/// USec per Month +pub const USEC_PER_MONTH: u64 = 2629800 * USEC_PER_SEC; +/// USec per Hour +pub const USEC_PER_HOUR: u64 = 60 * USEC_PER_MINUTE; +/// USec per Day +pub const USEC_PER_DAY: u64 = 24 * USEC_PER_HOUR; +/// USec per Week +pub const USEC_PER_WEEK: u64 = 7 * USEC_PER_DAY; +/// USec per Year +pub const USEC_PER_YEAR: u64 = 31557600 * USEC_PER_SEC; + +/// NSEC per Minute +pub const NSEC_PER_MINUTE: u64 = 60 * NSEC_PER_SEC; + +/// parse time +/// default_unit: convert to the specified time unit +pub fn parse_time(t: &str, default_unit: u64) -> Result { + if t.is_empty() { + return Err(Errno::EINVAL); + } + + let mut usec = 0; + let mut something = false; + let mut p = t.trim(); + let mut cstr_p; + + if p.starts_with("infinity") { + let (_, right) = p.split_at("infinity".len()); + let s = right.trim().to_string(); + if !s.is_empty() { + return Err(Errno::EINVAL); + } + return Ok(USEC_INFINITY); + } + + loop { + let mut multiplier = default_unit; + + p = p.trim_start(); + if p.is_empty() { + if !something { + return Err(Errno::EINVAL); + } + break; + } + + /* Don't allow "-0" */ + if p.starts_with('-') { + return Err(Errno::ERANGE); + } + + cstr_p = CString::new(p).unwrap(); + let mut endp: *mut c_char = std::ptr::null_mut(); + let (l, e) = unsafe { + let l = strtoll(cstr_p.as_ptr() as *const c_char, &mut endp, 10); + Errno::clear(); + let errno = nix::errno::errno(); + if errno > 0 { + return Err(nix::errno::from_i32(errno)); + } + + (l, CStr::from_ptr(endp).to_str().unwrap()) + }; + + if l < 0 { + return Err(Errno::ERANGE); + } + + if e.starts_with('.') { + let (_, e_right) = e.split_at(1); + p = e_right; + p = p.trim_start_matches(char::is_numeric); + } else if e == p { + return Err(Errno::EINVAL); + } else { + p = e; + } + + let s = p; + p = p.trim_start(); + extract_multiplier(&mut p, &mut multiplier); + + if s == p && !p.is_empty() { + /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */ + return Err(Errno::EINVAL); + } + + if l as u64 >= USEC_INFINITY / multiplier { + return Err(Errno::ERANGE); + } + + let mut k = l as u64 * multiplier; + if k >= USEC_INFINITY - usec { + return Err(Errno::ERANGE); + } + + usec += k; + + something = true; + + if e.starts_with('.') { + let mut m = multiplier / 10; + let (_, e_right) = e.split_at(1); + let e_right_byte = e_right.as_bytes(); + + /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */ + if e_right_byte.is_empty() || !e_right_byte[0].is_ascii_digit() { + return Err(Errno::EINVAL); + } + for b in e_right_byte.iter() { + if !b.is_ascii_digit() { + break; + } + + k = (*b as u64 - '0' as u64) * m; + if k >= USEC_INFINITY - usec { + return Err(Errno::ERANGE); + } + usec += k; + m /= 10; + } + } + } + Ok(usec) +} + +/// parse time to sec +pub fn parse_sec(t: &str) -> Result { + parse_time(t, USEC_PER_SEC) +} + +struct Table<'a> { + suffix: &'a str, + usec: u64, +} + +const TABLE: &[Table] = &[ + Table { + suffix: "seconds", + usec: USEC_PER_SEC, + }, + Table { + suffix: "sec", + usec: USEC_PER_SEC, + }, + Table { + suffix: "s", + usec: USEC_PER_SEC, + }, + Table { + suffix: "minutes", + usec: NSEC_PER_MINUTE, + }, + Table { + suffix: "minute", + usec: NSEC_PER_MINUTE, + }, + Table { + suffix: "min", + usec: NSEC_PER_MINUTE, + }, + Table { + suffix: "months", + usec: USEC_PER_MONTH, + }, + Table { + suffix: "month", + usec: USEC_PER_MONTH, + }, + Table { + suffix: "M", + usec: USEC_PER_MONTH, + }, + Table { + suffix: "msec", + usec: USEC_PER_MSEC, + }, + Table { + suffix: "ms", + usec: USEC_PER_MSEC, + }, + Table { + suffix: "m", + usec: USEC_PER_MINUTE, + }, + Table { + suffix: "hours", + usec: USEC_PER_HOUR, + }, + Table { + suffix: "hour", + usec: USEC_PER_HOUR, + }, + Table { + suffix: "hr", + usec: USEC_PER_HOUR, + }, + Table { + suffix: "h", + usec: USEC_PER_HOUR, + }, + Table { + suffix: "days", + usec: USEC_PER_DAY, + }, + Table { + suffix: "day", + usec: USEC_PER_DAY, + }, + Table { + suffix: "d", + usec: USEC_PER_DAY, + }, + Table { + suffix: "weeks", + usec: USEC_PER_WEEK, + }, + Table { + suffix: "week", + usec: USEC_PER_WEEK, + }, + Table { + suffix: "w", + usec: USEC_PER_WEEK, + }, + Table { + suffix: "years", + usec: USEC_PER_YEAR, + }, + Table { + suffix: "year", + usec: USEC_PER_YEAR, + }, + Table { + suffix: "y", + usec: USEC_PER_YEAR, + }, + Table { + suffix: "usec", + usec: 1, + }, + Table { + suffix: "us", + usec: 1, + }, + Table { + suffix: "µs", + usec: 1, + }, +]; + +fn extract_multiplier(p: &mut &str, multiplier: &mut u64) { + for table in TABLE { + if p.starts_with(table.suffix) { + *multiplier = table.usec; + let (_, e) = p.split_at(table.suffix.len()); + *p = e; + return; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_sec() { + let u = parse_sec("5s").unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC); + + let u = parse_sec("5s500ms").unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC); + + let u = parse_sec(" 5s 500ms ").unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC); + + let u = parse_sec(" 5.5s ").unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC); + + let u = parse_sec(" 5.5s 0.5ms ").unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC + 500 * USEC_PER_MSEC + 500); + + let u = parse_sec(" .22s ").unwrap(); + assert_eq!(u, 220 * USEC_PER_MSEC); + + let u = parse_sec(" .50y ").unwrap(); + assert_eq!(u, USEC_PER_YEAR / 2); + + let u = parse_sec("2.5").unwrap(); + assert_eq!(u, 2500 * USEC_PER_MSEC); + + let u = parse_sec(".7").unwrap(); + assert_eq!(u, 700 * USEC_PER_MSEC); + + let u = parse_sec("23us").unwrap(); + assert_eq!(u, 23); + + let u = parse_sec("23µs").unwrap(); + assert_eq!(u, 23); + + let u = parse_sec("infinity").unwrap(); + assert_eq!(u, USEC_INFINITY); + + let u = parse_sec(" infinity ").unwrap(); + assert_eq!(u, USEC_INFINITY); + + let u = parse_sec("+3.1s").unwrap(); + assert_eq!(u, 3100 * USEC_PER_MSEC); + + let u = parse_sec("3.1s.2").unwrap(); + assert_eq!(u, 3300 * USEC_PER_MSEC); + + let u = parse_sec("3.1 .2").unwrap(); + assert_eq!(u, 3300 * USEC_PER_MSEC); + + let u = parse_sec("3.1 sec .2 sec").unwrap(); + assert_eq!(u, 3300 * USEC_PER_MSEC); + + let u = parse_sec("3.1 sec 1.2 sec").unwrap(); + assert_eq!(u, 4300 * USEC_PER_MSEC); + + assert!(parse_sec(" xyz ").is_err()); + assert!(parse_sec("").is_err()); + assert!(parse_sec(" . ").is_err()); + assert!(parse_sec(" 5. ").is_err()); + assert!(parse_sec(".s ").is_err()); + assert!(parse_sec("-5s ").is_err()); + assert!(parse_sec("-0.3s ").is_err()); + assert!(parse_sec("-0.0s ").is_err()); + assert!(parse_sec("-0.-0s ").is_err()); + assert!(parse_sec("0.-0s ").is_err()); + assert!(parse_sec("3.-0s ").is_err()); + assert!(parse_sec(" infinity .7").is_err()); + assert!(parse_sec(".3 infinity").is_err()); + assert!(parse_sec("3.+1s").is_err()); + assert!(parse_sec("3. 1s").is_err()); + assert!(parse_sec("3.s").is_err()); + assert!(parse_sec("12.34.56").is_err()); + assert!(parse_sec("12..34").is_err()); + assert!(parse_sec("..1234").is_err()); + assert!(parse_sec("1234..").is_err()); + } + #[test] + fn test_parse_time() { + let u = parse_time("5", 1).unwrap(); + assert_eq!(u, 5); + + let u = parse_time("5", USEC_PER_MSEC).unwrap(); + assert_eq!(u, 5 * USEC_PER_MSEC); + + let u = parse_time("5", USEC_PER_SEC).unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC); + + let u = parse_time("5s", 1).unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC); + + let u = parse_time("5s", USEC_PER_SEC).unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC); + + let u = parse_time("5s", USEC_PER_MSEC).unwrap(); + assert_eq!(u, 5 * USEC_PER_SEC); + + assert_eq!(parse_time("11111111111111y", 1), Err(Errno::ERANGE)); + + let u = parse_time("1.1111111111111y", 1).unwrap(); + assert_eq!(u, 35063999999997); + } +} diff --git a/libs/constants/src/lib.rs b/libs/constants/src/lib.rs index 3be57232..46a3a50a 100644 --- a/libs/constants/src/lib.rs +++ b/libs/constants/src/lib.rs @@ -35,12 +35,3 @@ pub const INIT_PARA_PATH: &str = "/run/sysmaster/init_para"; /// invalid fd pub const INVALID_FD: i32 = -1; - -/// USec per Sec -pub const USEC_PER_SEC: u64 = 1000000; -/// NSec per USec -pub const NSEC_PER_USEC: u64 = 1000; -/// NSec per Sec -pub const NSEC_PER_SEC: u64 = 1000000000; -/// USec infinity -pub const USEC_INFINITY: u64 = u64::MAX; diff --git a/libs/event/Cargo.toml b/libs/event/Cargo.toml index 2609fbdc..fb8e343b 100644 --- a/libs/event/Cargo.toml +++ b/libs/event/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -constants = { path = "../constants" } +basic = { path = "../basic", default-features = false, features = ["time_util"]} libc = { version = "0.2", default-features = false } nix = { version = "0.24", default-features = false, features = [ diff --git a/libs/event/src/timer.rs b/libs/event/src/timer.rs index 53d42e97..c5f020c2 100644 --- a/libs/event/src/timer.rs +++ b/libs/event/src/timer.rs @@ -18,7 +18,7 @@ use std::{ }; use crate::{EventType, Source}; -use constants::{NSEC_PER_SEC, NSEC_PER_USEC, USEC_INFINITY, USEC_PER_SEC}; +use basic::time_util::{NSEC_PER_SEC, NSEC_PER_USEC, USEC_INFINITY, USEC_PER_SEC}; #[derive(Debug, Clone, Copy)] pub(crate) struct Timestamp { -- Gitee