From fe8441d6f4bd80d5208edfe9b3cc3ed39e7f0410 Mon Sep 17 00:00:00 2001 From: tanliyong Date: Tue, 9 Jan 2024 11:06:26 +0800 Subject: [PATCH 1/2] feature: support transient unit configuration file read and write framework --- core/coms/mount/src/manager.rs | 10 +- core/coms/service/src/bus.rs | 143 ++++++++++++ core/coms/service/src/config.rs | 10 + core/coms/service/src/lib.rs | 9 +- core/coms/service/src/manager.rs | 10 +- core/coms/service/src/rentry.rs | 26 +++ core/coms/service/src/unit.rs | 8 + core/coms/socket/src/manager.rs | 10 +- core/libcore/src/error.rs | 6 + core/libcore/src/unit/base.rs | 32 ++- core/libcore/src/unit/deps.rs | 18 ++ core/libcore/src/unit/mod.rs | 7 +- core/libcore/src/unit/umif.rs | 13 ++ core/libcore/src/unit/write.rs | 96 ++++++++ core/sysmaster/src/job/manager.rs | 4 +- core/sysmaster/src/plugin/mod.rs | 27 ++- core/sysmaster/src/unit/bus.rs | 284 ++++++++++++++++++++++++ core/sysmaster/src/unit/entry/base.rs | 27 ++- core/sysmaster/src/unit/entry/bus.rs | 33 +++ core/sysmaster/src/unit/entry/config.rs | 14 ++ core/sysmaster/src/unit/entry/load.rs | 105 ++++++++- core/sysmaster/src/unit/entry/mod.rs | 9 +- core/sysmaster/src/unit/entry/uentry.rs | 72 +++++- core/sysmaster/src/unit/entry/unitx.rs | 47 +++- core/sysmaster/src/unit/manager.rs | 228 ++++++------------- core/sysmaster/src/unit/mod.rs | 6 +- core/sysmaster/src/unit/rentry.rs | 133 ++++++++++- core/sysmaster/src/unit/submanager.rs | 179 +++++++++++++++ core/sysmaster/src/unit/uload.rs | 9 + core/sysmaster/src/unit/util/unit_om.rs | 8 +- libs/basic/src/fs.rs | 4 + 31 files changed, 1363 insertions(+), 224 deletions(-) create mode 100644 core/coms/service/src/bus.rs create mode 100644 core/libcore/src/unit/write.rs create mode 100644 core/sysmaster/src/unit/bus.rs create mode 100644 core/sysmaster/src/unit/entry/bus.rs create mode 100644 core/sysmaster/src/unit/submanager.rs diff --git a/core/coms/mount/src/manager.rs b/core/coms/mount/src/manager.rs index ab8f3039..d1a3ef40 100644 --- a/core/coms/mount/src/manager.rs +++ b/core/coms/mount/src/manager.rs @@ -81,7 +81,15 @@ impl ReStation for MountManager { } } -impl UnitManagerObj for MountManager {} +impl UnitManagerObj for MountManager { + fn private_section(&self, _unit_type: UnitType) -> String { + "Mount".into() + } + + fn can_transient(&self, _unit_type: UnitType) -> bool { + true + } +} // the declaration "pub(self)" is for identification only. impl MountManager { diff --git a/core/coms/service/src/bus.rs b/core/coms/service/src/bus.rs new file mode 100644 index 00000000..0028ca50 --- /dev/null +++ b/core/coms/service/src/bus.rs @@ -0,0 +1,143 @@ +// 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 super::comm::ServiceUnitComm; +use super::config::ServiceConfig; +use core::error::*; +use core::unit::{self, UnitWriteFlags}; +use std::rc::Rc; + +pub struct ServiceBus { + // associated objects + comm: Rc, + config: Rc, + // owned objects +} + +impl ServiceBus { + pub(super) fn new(commr: &Rc, configr: &Rc) -> ServiceBus { + ServiceBus { + comm: Rc::clone(commr), + config: Rc::clone(configr), + } + } + + pub(super) fn unit_set_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let mut ret = self.cgroup_set_transient_property(key, value, flags); + if let Err(Error::NotFound { what: _ }) = ret { + let unit = self.comm.owner().unwrap(); + if unit.transient() && unit.is_load_stub() { + ret = self.unit_set_transient_property(key, value, flags); + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.exec_set_transient_property(key, value, flags); + } + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.kill_set_transient_property(key, value, flags); + } + } + } + + ret + } + + fn unit_set_transient_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let real_flags = flags | UnitWriteFlags::PRIVATE; + match key { + "RemainAfterExit" + | "Type" + | "NotifyAccess" + | "PIDFile" + | "Restart" + | "RestartPreventExitStatus" => self.unit_write_property(key, value, real_flags, false), + "ExecStart" | "ExecStartPre" | "ExecStartPost" | "ExecStop" | "ExecStopPost" + | "ExecReload" | "ExecCondition" => { + self.unit_write_property(key, value, real_flags, false) + } + _ => Err(Error::NotFound { + what: "set transient property".to_string(), + }), + } + } + + fn exec_set_transient_property( + &self, + _key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: "set cgroup property".to_string(), + }) + } + + fn kill_set_transient_property( + &self, + _key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: "set cgroup property".to_string(), + }) + } + + fn cgroup_set_transient_property( + &self, + _key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: "set cgroup property".to_string(), + }) + } + + fn unit_write_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + update: bool, + ) -> Result<()> { + if unit::unit_write_flags_is_noop(flags) { + return Ok(()); + } + + let unit = self.comm.owner().unwrap(); + let um = self.comm.um(); + let ps = um.private_section(unit.unit_type()); + + if update { + self.set_property(key, value)?; + } + unit::unit_write_settingf(unit, &ps, flags, key, format_args!("{}={}", key, value)) + } + + fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.config.set_property(key, value) + } +} diff --git a/core/coms/service/src/config.rs b/core/coms/service/src/config.rs index ca776e5e..440f0dda 100644 --- a/core/coms/service/src/config.rs +++ b/core/coms/service/src/config.rs @@ -137,6 +137,12 @@ impl ServiceConfig { pub(super) fn pid_file(&self) -> Option { self.data.borrow().Service.PIDFile.clone() } + + pub(super) fn set_property(&self, key: &str, value: &str) -> Result<()> { + let ret = self.data.borrow_mut().set_property(key, value); + self.db_update(); + ret + } } fn specifier_escape_exec_command( @@ -198,6 +204,10 @@ impl ServiceConfigData { unit_specifier_data, ); } + + pub(self) fn set_property(&mut self, key: &str, value: &str) -> Result<()> { + self.Service.set_property(key, value) + } } #[cfg(test)] diff --git a/core/coms/service/src/lib.rs b/core/coms/service/src/lib.rs index 87809ff7..df2e35e1 100644 --- a/core/coms/service/src/lib.rs +++ b/core/coms/service/src/lib.rs @@ -74,11 +74,12 @@ compile_error!("feature plugin and noplugin cannot be enabled at the same time") pub use {manager::__um_obj_create, unit::__subunit_create_with_params}; // dependency: -// service_base -> service_rentry -> {service_comm | service_config} -// {service_pid | service_spawn} -> -// {service_mng} -> -// {service_monitor} -> service_unit -> service_manager +// base -> rentry -> {comm | config} +// {pid | spawn} -> +// {mng} -> +// {monitor} -> {bus -> unit} -> manager mod base; +mod bus; mod comm; mod config; mod manager; diff --git a/core/coms/service/src/manager.rs b/core/coms/service/src/manager.rs index 420b7c4e..12d137a4 100644 --- a/core/coms/service/src/manager.rs +++ b/core/coms/service/src/manager.rs @@ -17,7 +17,7 @@ use constants::LOG_FILE_PATH; use super::comm::ServiceUmComm; use core::rel::{ReStation, Reliability}; -use core::unit::{UmIf, UnitManagerObj, UnitMngUtil}; +use core::unit::{UmIf, UnitManagerObj, UnitMngUtil, UnitType}; use std::rc::Rc; use std::sync::Arc; @@ -36,7 +36,13 @@ impl ServiceManager { } impl UnitManagerObj for ServiceManager { - // nothing to customize + fn private_section(&self, _unit_type: UnitType) -> String { + "Service".into() + } + + fn can_transient(&self, _unit_type: UnitType) -> bool { + true + } } impl ReStation for ServiceManager { diff --git a/core/coms/service/src/rentry.rs b/core/coms/service/src/rentry.rs index bc1acccc..101c9a12 100644 --- a/core/coms/service/src/rentry.rs +++ b/core/coms/service/src/rentry.rs @@ -334,6 +334,32 @@ impl SectionService { self.TimeoutStopSec = time_out; } } + + pub(super) fn set_property(&mut self, key: &str, value: &str) -> Result<()> { + match key { + "RemainAfterExit" => self.RemainAfterExit = basic::config::parse_boolean(value)?, + "Type" => self.Type = ServiceType::parse_from_str(value)?, + "NotifyAccess" => self.NotifyAccess = Some(NotifyAccess::parse_from_str(value)?), + "PIDFile" => self.PIDFile = Some(parse_pidfile(value)?), + "Restart" => self.Restart = ServiceRestart::parse_from_str(value)?, + "RestartPreventExitStatus" => { + self.RestartPreventExitStatus = ExitStatusSet::parse_from_str(value)? + } + "ExecStart" => self.ExecStart = core::exec::parse_exec_command(value)?, + "ExecStartPre" => self.ExecStartPre = core::exec::parse_exec_command(value)?, + "ExecStartPost" => self.ExecStartPost = core::exec::parse_exec_command(value)?, + "ExecStop" => self.ExecStop = core::exec::parse_exec_command(value)?, + "ExecStopPost" => self.ExecStopPost = core::exec::parse_exec_command(value)?, + "ExecReload" => self.ExecReload = core::exec::parse_exec_command(value)?, + "ExecCondition" => self.ExecCondition = core::exec::parse_exec_command(value)?, + _ => { + return Err(Error::NotFound { + what: "set property".to_string(), + }) + } + }; + Ok(()) + } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/core/coms/service/src/unit.rs b/core/coms/service/src/unit.rs index 4c7db89c..47dd7169 100644 --- a/core/coms/service/src/unit.rs +++ b/core/coms/service/src/unit.rs @@ -12,6 +12,7 @@ use crate::rentry::ServiceRestart; +use super::bus::ServiceBus; use super::comm::ServiceUnitComm; use super::config::ServiceConfig; use super::mng::RunningData; @@ -22,6 +23,7 @@ use core::error::*; use core::rel::{ReStation, Reliability}; use core::unit::{ SubUnit, UmIf, UnitActiveState, UnitBase, UnitDependencyMask, UnitMngUtil, UnitRelations, + UnitWriteFlags, }; use nix::sys::signal::Signal; use nix::sys::socket::UnixCredentials; @@ -38,6 +40,7 @@ struct ServiceUnit { config: Rc, mng: Rc, exec_ctx: Rc, + bus: ServiceBus, } impl ReStation for ServiceUnit { @@ -187,6 +190,10 @@ impl SubUnit for ServiceUnit { fn release_socket_fd(&self, fd: i32) { self.mng.release_socket_fd(fd); } + + fn unit_set_property(&self, key: &str, value: &str, flags: UnitWriteFlags) -> Result<()> { + self.bus.unit_set_property(key, value, flags) + } } impl UnitMngUtil for ServiceUnit { @@ -213,6 +220,7 @@ impl ServiceUnit { config: Rc::clone(&config), mng: Rc::clone(&_mng), exec_ctx: Rc::clone(&context), + bus: ServiceBus::new(&comm, &config), } } diff --git a/core/coms/socket/src/manager.rs b/core/coms/socket/src/manager.rs index 7f9c4846..51bbb14c 100644 --- a/core/coms/socket/src/manager.rs +++ b/core/coms/socket/src/manager.rs @@ -18,7 +18,7 @@ use constants::LOG_FILE_PATH; use super::comm::SocketUmComm; use super::rentry::SocketReFrame; use core::rel::{ReStation, Reliability}; -use core::unit::{UmIf, UnitManagerObj, UnitMngUtil}; +use core::unit::{UmIf, UnitManagerObj, UnitMngUtil, UnitType}; use std::rc::Rc; use std::sync::Arc; @@ -37,7 +37,13 @@ impl SocketManager { } impl UnitManagerObj for SocketManager { - // nothing to customize + fn private_section(&self, _unit_type: UnitType) -> String { + "Socket".into() + } + + fn can_transient(&self, _unit_type: UnitType) -> bool { + true + } } impl ReStation for SocketManager { diff --git a/core/libcore/src/error.rs b/core/libcore/src/error.rs index 28a47d32..d5d78298 100644 --- a/core/libcore/src/error.rs +++ b/core/libcore/src/error.rs @@ -75,6 +75,11 @@ pub enum Error { source: std::io::Error, }, + #[snafu(display("FmtError(libcore)"))] + Fmt { + source: std::fmt::Error, + }, + #[snafu(display("NixError(libcore)"))] Nix { source: nix::Error, @@ -189,6 +194,7 @@ impl From for nix::Error { Error::Var { source: _ } => nix::Error::EINVAL, Error::Util { source: _ } => nix::Error::EINVAL, Error::Io { source: _ } => nix::Error::EIO, + Error::Fmt { source: _ } => nix::Error::EIO, Error::Nix { source } => source, Error::Heed { source: _ } => nix::Error::EIO, Error::InvalidData => nix::Error::EINVAL, diff --git a/core/libcore/src/unit/base.rs b/core/libcore/src/unit/base.rs index b62e29a5..e6d7c909 100644 --- a/core/libcore/src/unit/base.rs +++ b/core/libcore/src/unit/base.rs @@ -11,10 +11,10 @@ // See the Mulan PSL v2 for more details. use super::super::rel::ReStation; +use super::deps::{UnitType, UnitWriteFlags}; use super::kill::{KillContext, KillOperation}; use super::state::{UnitActiveState, UnitNotifyFlags}; use super::umif::UnitMngUtil; -use super::UnitType; use crate::error::*; use basic::time::UnitTimeStamp; use bitflags::bitflags; @@ -34,6 +34,8 @@ pub trait UnitBase { /// fn id(&self) -> String; /// + fn unit_type(&self) -> UnitType; + /// fn test_start_limit(&self) -> bool; /// fn reset_start_limit(&self); @@ -73,6 +75,21 @@ pub trait UnitBase { /// guess main pid from the cgroup path fn guess_main_pid(&self) -> Result; + /// + fn is_load_stub(&self) -> bool; + + /// + fn transient(&self) -> bool; + + /// + fn transient_file(&self) -> Option; + + /// + fn last_section_private(&self) -> i8; + + /// + fn set_last_section_private(&self, lsp: i8); + /// fn get_unit_timestamp(&self) -> Rc>; } @@ -166,6 +183,13 @@ pub trait SubUnit: ReStation + UnitMngUtil { false } + /// + fn unit_set_property(&self, _key: &str, _value: &str, _flags: UnitWriteFlags) -> Result<()> { + Err(Error::NotFound { + what: "set property".to_string(), + }) + } + // ================ ONLY VALID FOR SERVICE ================ /// fn set_socket_fd(&self, _fd: i32) {} @@ -198,11 +222,11 @@ macro_rules! declare_unitobj_plugin_with_param { #[cfg_attr(feature = "plugin", no_mangle)] pub fn __subunit_create_with_params( um: Rc, - ) -> *mut dyn $crate::unit::SubUnit { + ) -> *const dyn $crate::unit::SubUnit { let construcotr: fn(um: Rc) -> $unit_type = $constructor; let obj = construcotr(um); - let boxed: Box = Box::new(obj); - Box::into_raw(boxed) + let rced: Rc = Rc::new(obj); + Rc::into_raw(rced) } }; } diff --git a/core/libcore/src/unit/deps.rs b/core/libcore/src/unit/deps.rs index 876dfef1..bed8fa69 100644 --- a/core/libcore/src/unit/deps.rs +++ b/core/libcore/src/unit/deps.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use bitflags::bitflags; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, num::ParseIntError, str::FromStr}; @@ -193,3 +194,20 @@ pub fn unit_name_to_type(unit_name: &str) -> UnitType { } UnitType::from_str(words[words.len() - 1]).unwrap_or(UnitType::UnitTypeInvalid) } + +bitflags! { + /// flags used when writing files + pub struct UnitWriteFlags: u8 { + /// runtime file + const RUNTIME = 1 << 0; + /// persistent file + const PERSISTENT = 1 << 1; + /// sub-unit type section in file + const PRIVATE = 1 << 2; + } +} + +/// +pub fn unit_write_flags_is_noop(flags: UnitWriteFlags) -> bool { + !flags.intersects(UnitWriteFlags::RUNTIME | UnitWriteFlags::PERSISTENT) +} diff --git a/core/libcore/src/unit/mod.rs b/core/libcore/src/unit/mod.rs index a043be4b..0d130501 100644 --- a/core/libcore/src/unit/mod.rs +++ b/core/libcore/src/unit/mod.rs @@ -12,11 +12,15 @@ //! pub use base::{unit_name_is_valid, SubUnit, UnitBase, UnitNameFlags}; -pub use deps::{unit_name_to_type, UnitDependencyMask, UnitRelationAtom, UnitRelations, UnitType}; +pub use deps::{unit_name_to_type, + unit_write_flags_is_noop, UnitDependencyMask, UnitRelationAtom, UnitRelations, UnitType, + UnitWriteFlags, +}; pub use kill::{KillContext, KillMode, KillOperation}; pub use path_spec::{PathSpec, PathType}; pub use state::{UnitActiveState, UnitNotifyFlags, UnitStatus}; pub use umif::{UmIf, UnitManagerObj, UnitMngUtil}; +pub use write::{unit_write_setting, unit_write_settingf}; mod base; mod deps; @@ -24,3 +28,4 @@ mod kill; mod path_spec; mod state; mod umif; +mod write; diff --git a/core/libcore/src/unit/umif.rs b/core/libcore/src/unit/umif.rs index 1468bd9f..9af9bc14 100644 --- a/core/libcore/src/unit/umif.rs +++ b/core/libcore/src/unit/umif.rs @@ -211,6 +211,11 @@ pub trait UmIf { Ok(()) } + /// + fn private_section(&self, _unit_type: UnitType) -> String { + null_str!("") + } + /* ========== ONLY VALID IN SERVICE ========== */ /// set the service's socket fd fn service_set_socket_fd(&self, _service_name: &str, _fd: i32) {} @@ -271,6 +276,14 @@ pub trait UnitManagerObj: UnitMngUtil + ReStation { } /// fn shutdown(&self) {} + /// + fn private_section(&self, _unit_type: UnitType) -> String { + null_str!("") + } + /// + fn can_transient(&self, _unit_type: UnitType) -> bool { + false + } } /// the macro for create a sub unit-manager instance diff --git a/core/libcore/src/unit/write.rs b/core/libcore/src/unit/write.rs new file mode 100644 index 00000000..26e5e33f --- /dev/null +++ b/core/libcore/src/unit/write.rs @@ -0,0 +1,96 @@ +// 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 super::base::UnitBase; +use super::deps::{self, UnitWriteFlags}; +use crate::error::*; +use std::fmt::Write as _; +use std::fmt::{self, Arguments}; +use std::fs::OpenOptions; +use std::io::Write as _; +use std::rc::Rc; + +/// +pub fn unit_write_setting( + unit: Rc, + ps: &str, + flags: UnitWriteFlags, + _name: &str, + data: &str, +) -> Result<()> { + if deps::unit_write_flags_is_noop(flags) { + return Ok(()); + } + + let mut prefix = String::new(); + let private_section = String::from(ps); + let transient_file = unit.transient_file(); + let last_section_private = unit.last_section_private(); + + if flags.contains(UnitWriteFlags::PRIVATE) { + if private_section.is_empty() { + return Err(Error::InvalidData); + } + + if transient_file.is_none() || last_section_private < 0 { + //write!(&mut prefix, "[{}]\n", private_section).context(FmtSnafu)?; + writeln!(&mut prefix, "[{}]", private_section).context(FmtSnafu)?; + } else if last_section_private == 0 { + //write!(&mut prefix, "\n[{}]\n", private_section).context(FmtSnafu)?; + writeln!(&mut prefix, "\n[{}]", private_section).context(FmtSnafu)?; + } + } else if !flags.is_empty() { + // make clippy "collapsible_else_if" happy + if transient_file.is_none() || last_section_private < 0 { + //write!(&mut prefix, "[Unit]\n").context(FmtSnafu)?; + writeln!(&mut prefix, "[Unit]").context(FmtSnafu)?; + } else if last_section_private > 0 { + //write!(&mut prefix, "\n[Unit]\n").context(FmtSnafu)?; + writeln!(&mut prefix, "\n[Unit]").context(FmtSnafu)?; + } + } else { + // guaranteed by unit_write_flags_is_noop. + unreachable!(); + } + + if let Some(tf) = transient_file { + let mut file = OpenOptions::new().append(true).open(tf).context(IoSnafu)?; + write!(file, "{}{}", prefix, data).context(IoSnafu)?; + if !data.ends_with('\n') { + //write!(file, "\n").context(IoSnafu)?; + writeln!(file).context(IoSnafu)?; // append \n + } + + unit.set_last_section_private(flags.contains(UnitWriteFlags::PRIVATE) as i8); + return Ok(()); + } + + // not supported now + Err(Error::NotSupported) +} + +/// +pub fn unit_write_settingf( + unit: Rc, + ps: &str, + flags: UnitWriteFlags, + name: &str, + args: Arguments<'_>, +) -> Result<()> { + if deps::unit_write_flags_is_noop(flags) { + return Ok(()); + } + + let mut data = String::new(); + fmt::write(&mut data, args).context(FmtSnafu)?; + unit_write_setting(unit, ps, flags, name, &data) +} diff --git a/core/sysmaster/src/job/manager.rs b/core/sysmaster/src/job/manager.rs index bcf2d9db..1d627d57 100644 --- a/core/sysmaster/src/job/manager.rs +++ b/core/sysmaster/src/job/manager.rs @@ -204,7 +204,9 @@ impl JobManager { } pub(crate) fn has_job(&self, unit: &Rc) -> bool { - !self.data.jobs.get_suspends(unit).is_empty() + let trigger = self.data.jobs.get_trigger_info(unit).is_some(); + let suspend = !self.data.jobs.get_suspends(unit).is_empty(); + trigger || suspend } pub(crate) fn has_stop_job(&self, unit: &Rc) -> bool { diff --git a/core/sysmaster/src/plugin/mod.rs b/core/sysmaster/src/plugin/mod.rs index d2b62182..48b8fc71 100644 --- a/core/sysmaster/src/plugin/mod.rs +++ b/core/sysmaster/src/plugin/mod.rs @@ -158,15 +158,19 @@ impl Plugin { let mut ret: String = String::with_capacity(256); #[cfg(test)] let lib_path_devel = { - let devel_path = |out_dir: &str| { - if out_dir.contains("build") { - let _tmp: Vec<_> = out_dir.split("build").collect(); - String::from(_tmp[0]) + let devel_path = |out_dir: Option<&str>| { + if let Some(outdir) = out_dir { + if outdir.contains("build") { + let _tmp: Vec<_> = outdir.split("build").collect(); + String::from(_tmp[0]) + } else { + outdir.to_string() + } } else { - out_dir.to_string() + String::new() } }; - devel_path(env!("OUT_DIR")) + devel_path(option_env!("OUT_DIR")) }; #[cfg(not(test))] @@ -451,7 +455,7 @@ impl Plugin { &self, unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { type FnType = fn(um: Rc) -> *mut dyn SubUnit; let dy_lib = match self.get_lib(unit_type) { @@ -480,8 +484,8 @@ impl Plugin { }; log::debug!("create unit obj with params"); - let boxed_raw = fun(um.clone()); - Ok(unsafe { Box::from_raw(boxed_raw) }) + let rced_raw = fun(um.clone()); + Ok(unsafe { Rc::from_raw(rced_raw) }) } /// Create a obj for subclasses of unit manager /// each sub unit manager need reference of declare_umobj_plugin @@ -575,6 +579,7 @@ mod tests { fn init_test() -> Arc { log::init_log( + "test_plugin", log::Level::Trace, vec!["console", "syslog"], "", @@ -589,8 +594,8 @@ mod tests { fn test_plugin_load_library() { let t_p = init_test(); let mf = env!("CARGO_MANIFEST_DIR"); - let out_dir = env!("OUT_DIR"); - log::info!("{},{}", out_dir, mf); + let out_dir = option_env!("OUT_DIR"); + log::info!("{:?},{}", out_dir, mf); for key in (*t_p.load_libs.read().unwrap()).keys() { // let service_unit = u_box.as_any().downcast_ref::().unwrap(); // assert_eq!(service_unit.get_unit_name(),""); diff --git a/core/sysmaster/src/unit/bus.rs b/core/sysmaster/src/unit/bus.rs new file mode 100644 index 00000000..0caa110e --- /dev/null +++ b/core/sysmaster/src/unit/bus.rs @@ -0,0 +1,284 @@ +// 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 super::super::job::JobManager; +use super::entry::UnitX; +use super::rentry::{self, UnitLoadState}; +use super::submanager::UnitSubManagers; +use super::uload::UnitLoad; +use basic::fs_util::RUN_TRANSIENT_PATH; +use core::error::*; +use core::rel::Reliability; +use core::unit::{self, UnitType, UnitWriteFlags}; +use nix::sys::stat::{self, Mode}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +pub struct UnitBus { + // associated objects + reli: Rc, + load: Rc, + jm: Rc, + sms: Rc, + // owned objects +} + +impl UnitBus { + pub(super) fn new( + relir: &Rc, + loadr: &Rc, + jmr: &Rc, + smsr: &Rc, + ) -> UnitBus { + UnitBus { + reli: Rc::clone(relir), + load: Rc::clone(loadr), + jm: Rc::clone(jmr), + sms: Rc::clone(smsr), + } + } + + pub(super) fn transient_unit_from_message( + &self, + properties: &[(&str, &str)], + name: &str, + ) -> Result> { + let unit_type = rentry::unit_name_to_type(name); + if unit_type == UnitType::UnitTypeInvalid { + return Err(Error::InvalidData); + } + + if !self.sms.can_transient(unit_type) { + return Err(Error::InvalidData); + } + + // the first incomplete loading + let unit = match self.load.load_unit(name) { + None => { + return Err(Error::UnitActionENoent); + } + Some(v) => v, + }; + + // prevent duplicate actions + if !self.unit_is_pristine(&unit) { + return Err(Error::UnitActionEAlready); + } + + // set configuration file of the transient unit + self.unit_make_transient(&unit, properties)?; + + // the second real loading + self.load.load_update(&unit); + + Ok(unit) + } + + fn unit_is_pristine(&self, unit: &Rc) -> bool { + let load_state = unit.load_state(); + let load = load_state == UnitLoadState::Loaded || load_state == UnitLoadState::NotFound; + let exist = !load + || !unit.load_paths().is_empty() + || unit.merged_into().is_some() + || self.jm.has_job(unit); + !exist + } + + fn unit_make_transient(&self, unit: &Rc, properties: &[(&str, &str)]) -> Result<()> { + self.reli.set_last_unit(&unit.id()); + let ret = self.unit_make_transient_body(unit, properties); + self.reli.clear_last_unit(); + if ret.is_err() { + unit.remove_transient(); + } + ret + } + + fn unit_make_transient_body( + &self, + unit: &Rc, + properties: &[(&str, &str)], + ) -> Result<()> { + let name = unit.id(); + let path = get_transient_file_path(&name); + + unit.make_transient(Some(path)); // record first + create_transient_file(&name)?; // create file + self.unit_set_properties(unit, properties, UnitWriteFlags::RUNTIME)?; // write file + + Ok(()) + } + + fn unit_set_properties( + &self, + unit: &Rc, + properties: &[(&str, &str)], + flags: UnitWriteFlags, + ) -> Result<()> { + for property in properties { + self.unit_set_property(unit, property.0, property.1, flags)?; + } + + Ok(()) + } + + fn unit_set_property( + &self, + unit: &Rc, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let mut ret = unit.set_sub_property(key, value, flags); + if let Err(Error::NotFound { what: _ }) = ret { + if unit.transient() && unit.load_state() == UnitLoadState::Stub { + ret = self.unit_set_transient_property(unit, key, value, flags); + } + } + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.unit_set_live_property(unit, key, value, flags); + } + + ret + } + + fn unit_set_transient_property( + &self, + unit: &Rc, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let ps = self.sms.private_section(unit.unit_type()); + match key { + "RefuseManualStart" + | "RefuseManualStop" + | "DefaultDependencies" + | "OnSuccessJobMode" + | "OnFailureJobMode" + | "IgnoreOnIsolate" + | "JobTimeoutAction" + | "StartLimitBurst" + | "StartLimitAction" + | "FailureAction" + | "SuccessAction" + | "ConditionACPower" + | "ConditionCapability" + | "ConditionDirectoryNotEmpty" + | "ConditionFileIsExecutable" + | "ConditionFileNotEmpty" + | "ConditionFirstBoot" + | "ConditionKernelCommandLine" + | "ConditionNeedsUpdate" + | "ConditionPathExists" + | "ConditionPathExistsGlob" + | "ConditionPathIsDirectory" + | "ConditionPathIsMountPoint" + | "ConditionPathIsReadWrite" + | "ConditionPathIsSymbolicLink" + | "ConditionSecurity" + | "ConditionUser" + | "AssertPathExists" + | "Documentation" => self.unit_write_property(unit, &ps, key, value, flags, false), + "Wants" | "Requires" | "BindsTo" | "Requisite" | "PartOf" | "OnFailure" + | "OnSuccess" | "Before" | "After" | "Conflicts" => { + self.unit_write_property(unit, &ps, key, value, flags, false) + } + _ => Err(Error::NotFound { + what: "set transient property".to_string(), + }), + } + } + + fn unit_set_live_property( + &self, + unit: &Rc, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let ps = self.sms.private_section(unit.unit_type()); + match key { + "Description" => self.unit_write_property(unit, &ps, key, value, flags, true), + _ => Err(Error::NotFound { + what: "set live property".to_string(), + }), + } + } + + fn unit_write_property( + &self, + unit: &Rc, + ps: &str, + key: &str, + value: &str, + flags: UnitWriteFlags, + update: bool, + ) -> Result<()> { + if unit::unit_write_flags_is_noop(flags) { + return Ok(()); + } + + if update { + unit.set_property(key, value)?; + } + unit.write_settingf(ps, flags, key, format_args!("{}={}", key, value)) + } +} + +fn create_transient_file(name: &str) -> Result<()> { + // create '/run/sysmaster/transient' with mode 750 + let old_mask = stat::umask(Mode::from_bits_truncate(!0o750)); + let ret = create_transient_dir_body(); + let _ = stat::umask(old_mask); + if let Err(e) = ret { + log::error!("create transient directory failed: {}", e); + return Err(e); + } + + // create '/run/sysmaster/transient/unit.service' with mode 640 + let old_mask = stat::umask(Mode::from_bits_truncate(!0o640)); + let ret = create_transient_file_body(name); + let _ = stat::umask(old_mask); + if let Err(e) = ret { + log::error!("create transient file failed:dir{:?}, {}", name, e); + return Err(e); + } + + Ok(()) +} + +fn create_transient_dir_body() -> Result<()> { + let dir = Path::new(RUN_TRANSIENT_PATH); + if !dir.exists() { + fs::create_dir_all(dir).context(IoSnafu)?; + log::info!( + "create transient directory successfully: {}.", + RUN_TRANSIENT_PATH + ); + } + + Ok(()) +} + +fn create_transient_file_body(name: &str) -> Result<()> { + let path = get_transient_file_path(name); + fs::write(path.clone(), "# This is a transient unit file, created programmatically via the sysmaster API. Do not edit.\n")?; + log::info!("create transient file successfully: {:?}.", path); + Ok(()) +} + +fn get_transient_file_path(name: &str) -> PathBuf { + Path::new(RUN_TRANSIENT_PATH).join(name) +} diff --git a/core/sysmaster/src/unit/entry/base.rs b/core/sysmaster/src/unit/entry/base.rs index 9fdf9704..7c322216 100644 --- a/core/sysmaster/src/unit/entry/base.rs +++ b/core/sysmaster/src/unit/entry/base.rs @@ -70,11 +70,28 @@ impl UeBase { self.unit_type } - pub(super) fn rentry_load_insert(&self, load_state: UnitLoadState) { - self.rentry.load_insert(&self.id.borrow(), load_state); - } - - pub(super) fn rentry_load_get(&self) -> Option { + pub(super) fn rentry_load_insert( + &self, + load_state: UnitLoadState, + transient: bool, + paths: Vec, + transient_file: Option, + last_section_private: i8, + ) { + self.rentry.load_insert( + &self.id.borrow(), + load_state, + transient, + paths, + transient_file, + last_section_private, + ); + } + + #[allow(clippy::type_complexity)] + pub(super) fn rentry_load_get( + &self, + ) -> Option<(UnitLoadState, bool, Vec, Option, i8)> { self.rentry.load_get(&self.id.borrow()) } diff --git a/core/sysmaster/src/unit/entry/bus.rs b/core/sysmaster/src/unit/entry/bus.rs new file mode 100644 index 00000000..95fbd204 --- /dev/null +++ b/core/sysmaster/src/unit/entry/bus.rs @@ -0,0 +1,33 @@ +// 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 super::config::UeConfig; +use core::error::*; +use std::rc::Rc; + +pub struct UeBus { + // associated objects + config: Rc, + // owned objects +} + +impl UeBus { + pub(super) fn new(configr: &Rc) -> UeBus { + UeBus { + config: Rc::clone(configr), + } + } + + pub(super) fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.config.set_property(key, value) + } +} diff --git a/core/sysmaster/src/unit/entry/config.rs b/core/sysmaster/src/unit/entry/config.rs index d336d68b..067f6e80 100644 --- a/core/sysmaster/src/unit/entry/config.rs +++ b/core/sysmaster/src/unit/entry/config.rs @@ -195,6 +195,12 @@ impl UeConfig { pub(crate) fn config_data(&self) -> Rc> { self.data.clone() } + + pub(crate) fn set_property(&self, key: &str, value: &str) -> Result<()> { + let ret = self.data.borrow_mut().set_property(key, value); + self.db_update(); + ret + } } #[derive(UnitConfig, Default, Debug)] @@ -245,6 +251,14 @@ impl UeConfigData { self.Unit.After = ret; } } + + pub(self) fn set_property(&mut self, key: &str, value: &str) -> Result<()> { + let mut ret = self.Unit.set_property(key, value); + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.Install.set_property(key, value); + } + ret + } } #[cfg(test)] diff --git a/core/sysmaster/src/unit/entry/load.rs b/core/sysmaster/src/unit/entry/load.rs index dc0d64ae..f48fb09a 100644 --- a/core/sysmaster/src/unit/entry/load.rs +++ b/core/sysmaster/src/unit/entry/load.rs @@ -15,10 +15,13 @@ use super::config::UeConfig; use crate::unit::data::{DataManager, UnitDepConf}; use crate::unit::rentry::{UnitLoadState, UnitRePps}; use crate::unit::util::UnitFile; +use basic::do_entry_log; use core::error::*; use core::rel::ReStation; use core::unit::UnitRelations; use std::cell::RefCell; +use std::fs::{self, OpenOptions}; +use std::io::Write; use std::path::PathBuf; use std::rc::Rc; @@ -31,9 +34,16 @@ pub(super) struct UeLoad { config: Rc, // owned objects + /* constant after loading */ + transient: RefCell, + paths: RefCell>, + /* changes with stages */ load_state: RefCell, in_load_queue: RefCell, in_target_dep_queue: RefCell, + /* temporarily present during loading stage */ + transient_file: RefCell>, + last_section_private: RefCell, // <0, nothing has been wrote; 0, in [Unit] section; >0, in [unit type-specific] section } impl ReStation for UeLoad { @@ -44,13 +54,25 @@ impl ReStation for UeLoad { if reload { return; } - if let Some(load_state) = self.base.rentry_load_get() { + if let Some((load_state, transient, paths, transient_file, last_section_private)) = + self.base.rentry_load_get() + { *self.load_state.borrow_mut() = load_state; + *self.transient.borrow_mut() = transient; + *self.paths.borrow_mut() = paths; + *self.transient_file.borrow_mut() = transient_file; + *self.last_section_private.borrow_mut() = last_section_private; } } fn db_insert(&self) { - self.base.rentry_load_insert(*self.load_state.borrow()); + self.base.rentry_load_insert( + *self.load_state.borrow(), + *self.transient.borrow(), + self.paths.borrow().clone(), + self.transient_file.borrow().clone(), + *self.last_section_private.borrow(), + ); } // reload: no external connections, no entry @@ -68,9 +90,13 @@ impl UeLoad { file: Rc::clone(filer), base: Rc::clone(baser), config: Rc::clone(config), + transient: RefCell::new(false), + paths: RefCell::new(Vec::new()), load_state: RefCell::new(UnitLoadState::Stub), in_load_queue: RefCell::new(false), in_target_dep_queue: RefCell::new(false), + transient_file: RefCell::new(None), + last_section_private: RefCell::new(-1), }; load.db_insert(); let flags = UnitRePps::QUEUE_LOAD | UnitRePps::QUEUE_TARGET_DEPS; @@ -137,6 +163,7 @@ impl UeLoad { self.config .load_fragment_and_dropin(self.file.as_ref(), &self.base.id())?; self.parse(); + self.set_paths(self.file.get_unit_id_fragment_pathbuf(&self.base.id())); Ok(()) } @@ -153,6 +180,80 @@ impl UeLoad { *self.in_target_dep_queue.borrow() } + pub(super) fn paths(&self) -> Vec { + self.paths.borrow().clone() + } + + pub(super) fn transient(&self) -> bool { + *self.transient.borrow() + } + + pub(super) fn transient_file(&self) -> Option { + self.transient_file.borrow().clone() + } + + pub(super) fn last_section_private(&self) -> i8 { + *self.last_section_private.borrow() + } + + pub(super) fn set_last_section_private(&self, lsp: i8) { + *self.last_section_private.borrow_mut() = lsp; + self.db_update(); + } + + pub(super) fn make_transient(&self, path: Option) { + // paths = fragment only + let mut paths = Vec::new(); + if let Some(p) = path.clone() { + paths.push(p); + } + + self.set_transient_file(path); + self.set_paths(paths); + self.set_load_state(UnitLoadState::Stub); + self.set_transient(true); + } + + pub(super) fn finalize_transient(&self) -> Result<()> { + if let Some(tf) = self.transient_file() { + let mut file = OpenOptions::new().write(true).open(tf).context(IoSnafu)?; + file.flush().context(IoSnafu)?; + self.set_transient_file(None); + } + + Ok(()) + } + + pub(super) fn remove_transient(&self) { + if !self.transient() { + return; + } + + let mut paths = self.paths(); + if paths.is_empty() { + return; + } + + // fragment + let fragment = paths.remove(0); + do_entry_log!(fs::remove_file, fragment, "remove"); + } + + fn set_paths(&self, paths: Vec) { + *self.paths.borrow_mut() = paths; + self.db_update(); + } + + fn set_transient(&self, transient: bool) { + *self.transient.borrow_mut() = transient; + self.db_update(); + } + + fn set_transient_file(&self, path: Option) { + *self.transient_file.borrow_mut() = path; + self.db_update(); + } + fn parse(&self) { let mut ud_conf = UnitDepConf::new(); // need get config from config database,and update depends hereW let config_data = self.config.config_data(); diff --git a/core/sysmaster/src/unit/entry/mod.rs b/core/sysmaster/src/unit/entry/mod.rs index 1e9fb637..e88db080 100644 --- a/core/sysmaster/src/unit/entry/mod.rs +++ b/core/sysmaster/src/unit/entry/mod.rs @@ -32,7 +32,7 @@ //! sysmaster refers to systemd. The preliminary plan includes 9 types of units. The naming rule of each type of configuration file is *. XXX. XXX refers to the specific unit type, such as service, slice, target, etc. //! The following modules are included. //! u_entry: The interface abstract entity of unit, which is the parent class of all units, can implement the SubUnit trait object -//! uf_interface: The interface is an internally managed entity object that encapsulates the Unit. Only UnitX objects can be seen in sysmaster, but the Unit cannot be seen. The Unit is isolated +//! unitx: The interface is an internally managed entity object that encapsulates the Unit. Only UnitX objects can be seen in sysmaster, but the Unit cannot be seen. The Unit is isolated //! uu_load: Encapsulates Unitload Status //! uu_child: The child maintains the parent and child processes associated with the unit. The child services associated with the unit may start the child processes. Therefore, it is necessary to maintain the processes associated with the unit. //! uu_cgroup: cgroup related configurations @@ -45,10 +45,11 @@ pub(crate) use unitx::UnitX; // pub(super) use uu_config::UnitConfigItem; // dependency: -// uu_condition -> -// uu_base -> {uu_config | uu_cgroup} -> {uu_load | uu_child} -> -// u_interface->u_entry -> {uf_interface} +// condition -> +// base -> {config | cgroup} -> {load | child | ratelimit} -> +// {bus -> uentry} -> {unitx} mod base; +mod bus; mod cgroup; mod child; mod condition; diff --git a/core/sysmaster/src/unit/entry/uentry.rs b/core/sysmaster/src/unit/entry/uentry.rs index 1d3cc7ce..1f16200a 100644 --- a/core/sysmaster/src/unit/entry/uentry.rs +++ b/core/sysmaster/src/unit/entry/uentry.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use super::base::UeBase; +use super::bus::UeBus; use super::cgroup::UeCgroup; use super::child::UeChild; use super::condition::{assert_keys::*, condition_keys::*, UeCondition}; @@ -26,7 +27,7 @@ use basic::time::{now_clockid, UnitTimeStamp}; use cgroup::{self, CgFlags}; use core::error::*; use core::rel::ReStation; -use core::unit::{KillContext, KillMode, KillOperation, UnitNotifyFlags}; +use core::unit::{KillContext, KillMode, KillOperation, UnitNotifyFlags, UnitWriteFlags}; use core::unit::{SubUnit, UnitActiveState, UnitBase, UnitType}; use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; use nix::sys::socket::UnixCredentials; @@ -49,15 +50,16 @@ pub struct Unit { base: Rc, config: Rc, - load: UeLoad, + load: Rc, child: UeChild, cgroup: UeCgroup, conditions: Rc, start_limit: StartLimit, - sub: Box, + sub: Rc, merged_into: RefCell>>, in_stop_when_bound_queue: RefCell, timestamp: Rc>, + bus: UeBus, } impl PartialEq for Unit { @@ -133,6 +135,10 @@ impl UnitBase for Unit { self.id() } + fn unit_type(&self) -> UnitType { + self.unit_type() + } + /*fn get_dependency_list(&self, _unit_name: &str, _atom: libcore::unit::UnitRelationAtom) -> Vec> { todo!() }*/ @@ -192,6 +198,26 @@ impl UnitBase for Unit { fn get_unit_timestamp(&self) -> Rc> { self.get_unit_timestamp() } + + fn is_load_stub(&self) -> bool { + self.load.load_state() == UnitLoadState::Stub + } + + fn transient(&self) -> bool { + self.load.transient() + } + + fn transient_file(&self) -> Option { + self.load.transient_file() + } + + fn last_section_private(&self) -> i8 { + self.load.last_section_private() + } + + fn set_last_section_private(&self, lsp: i8) { + self.load.set_last_section_private(lsp); + } } impl Unit { @@ -202,23 +228,25 @@ impl Unit { dmr: &Rc, rentryr: &Rc, filer: &Rc, - sub: Box, + subr: &Rc, ) -> Rc { let _base = Rc::new(UeBase::new(rentryr, String::from(name), unit_type)); let _config = Rc::new(UeConfig::new(&_base)); + let _load = Rc::new(UeLoad::new(dmr, filer, &_base, &_config)); let _u = Rc::new(Unit { dm: Rc::clone(dmr), base: Rc::clone(&_base), config: Rc::clone(&_config), - load: UeLoad::new(dmr, filer, &_base, &_config), + load: Rc::clone(&_load), child: UeChild::new(&_base), cgroup: UeCgroup::new(&_base), conditions: Rc::new(UeCondition::new()), - sub, + sub: Rc::clone(subr), start_limit: StartLimit::new(), merged_into: RefCell::new(None), in_stop_when_bound_queue: RefCell::new(false), timestamp: Rc::new(RefCell::new(UnitTimeStamp::default())), + bus: UeBus::new(&_config), }); let owner = Rc::clone(&_u); _u.sub.attach_unit(owner); @@ -632,6 +660,7 @@ impl Unit { pub(super) fn load_unit(&self) -> Result<()> { self.set_in_load_queue(false); + self.load.finalize_transient()?; match self.load.load_unit_confs() { Ok(_) => { let paths = self.load.get_unit_id_fragment_pathbuf(); @@ -788,10 +817,26 @@ impl Unit { self.load.load_state() } + pub(super) fn load_paths(&self) -> Vec { + self.load.paths() + } + + pub(super) fn transient(&self) -> bool { + self.load.transient() + } + pub fn set_load_state(&self, state: UnitLoadState) { self.load.set_load_state(state) } + pub(super) fn make_transient(&self, path: Option) { + self.load.make_transient(path) + } + + pub(super) fn remove_transient(&self) { + self.load.remove_transient() + } + pub(super) fn child_add_pids(&self, pid: Pid) { self.child.add_pids(pid); } @@ -853,6 +898,19 @@ impl Unit { pub fn get_unit_timestamp(&self) -> Rc> { Rc::clone(&self.timestamp) } + + pub(crate) fn set_sub_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + self.sub.unit_set_property(key, value, flags) + } + + pub(crate) fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.bus.set_property(key, value) + } } #[cfg(test)] @@ -898,7 +956,7 @@ mod tests { &Rc::new(dm), &rentry, &Rc::new(unit_file), - sub_obj, + &sub_obj, ) } diff --git a/core/sysmaster/src/unit/entry/unitx.rs b/core/sysmaster/src/unit/entry/unitx.rs index 68af9f3e..67a93389 100644 --- a/core/sysmaster/src/unit/entry/unitx.rs +++ b/core/sysmaster/src/unit/entry/unitx.rs @@ -20,9 +20,10 @@ use crate::unit::util::UnitFile; use basic::IN_SET; use core::error::*; use core::rel::ReStation; -use core::unit::{SubUnit, UnitActiveState, UnitRelations, UnitType}; +use core::unit::{self, SubUnit, UnitActiveState, UnitRelations, UnitType, UnitWriteFlags}; use nix::sys::wait::WaitStatus; use nix::unistd::Pid; +use std::fmt::Arguments; use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; @@ -62,9 +63,9 @@ impl UnitX { filer: &Rc, unit_type: UnitType, name: &str, - subclass: Box, + subclass: Rc, ) -> UnitX { - let unit = Unit::new(unit_type, name, dmr, rentryr, filer, subclass); + let unit = Unit::new(unit_type, name, dmr, rentryr, filer, &subclass); UnitX(unit) } @@ -246,10 +247,26 @@ impl UnitX { self.0.load_state() } + pub(crate) fn load_paths(&self) -> Vec { + self.0.load_paths() + } + + pub(crate) fn transient(&self) -> bool { + self.0.transient() + } + pub(crate) fn set_load_state(&self, state: UnitLoadState) { self.0.set_load_state(state) } + pub(crate) fn make_transient(&self, path: Option) { + self.0.make_transient(path) + } + + pub(crate) fn remove_transient(&self) { + self.0.remove_transient() + } + pub(crate) fn unit_type(&self) -> UnitType { self.0.unit_type() } @@ -274,6 +291,30 @@ impl UnitX { self.0.child_remove_pids(pid); } + pub(crate) fn set_sub_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + self.0.set_sub_property(key, value, flags) + } + + pub(crate) fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.0.set_property(key, value) + } + + pub(crate) fn write_settingf( + &self, + ps: &str, + flags: UnitWriteFlags, + name: &str, + args: Arguments<'_>, + ) -> Result<()> { + let unit = self.unit(); + unit::unit_write_settingf(unit, ps, flags, name, args) + } + pub(crate) fn unit(&self) -> Rc { Rc::clone(&self.0) } diff --git a/core/sysmaster/src/unit/manager.rs b/core/sysmaster/src/unit/manager.rs index 6b356f1d..cc5c74b0 100644 --- a/core/sysmaster/src/unit/manager.rs +++ b/core/sysmaster/src/unit/manager.rs @@ -20,6 +20,7 @@ /// ---->rentry /// use super::super::job::{JobAffect, JobConf, JobKind, JobManager}; +use super::bus::UnitBus; use super::datastore::UnitDb; use super::entry::{StartLimitResult, Unit, UnitEmergencyAction, UnitX}; use super::execute::ExecSpawn; @@ -27,6 +28,7 @@ use super::notify::NotifyManager; use super::rentry::{JobMode, UnitLoadState, UnitRe}; use super::runtime::UnitRT; use super::sigchld::Sigchld; +use super::submanager::UnitSubManagers; use super::uload::UnitLoad; use crate::job::JobResult; use crate::manager::config::ManagerConfig; @@ -59,7 +61,7 @@ use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; use std::rc::Rc; use std::str::FromStr; -use unit_submanager::UnitSubManagers; + //#[derive(Debug)] pub(crate) struct UnitManagerX { dm: Rc, @@ -304,13 +306,13 @@ pub struct UnitManager { rentry: Rc, db: Rc, rt: Rc, - load: UnitLoad, + load: Rc, jm: Rc, exec: ExecSpawn, sigchld: Sigchld, notify: NotifyManager, - sms: UnitSubManagers, - manager_config: Rc>, + sms: Rc, + bus: UnitBus, } impl UmIf for UnitManager { @@ -689,6 +691,10 @@ impl UmIf for UnitManager { Ok(()) } + fn private_section(&self, unit_type: UnitType) -> String { + self.sms.private_section(unit_type) + } + /// set the service's socket fd fn service_set_socket_fd(&self, service_name: &str, fd: i32) { let service = match self.units_get(service_name) { @@ -975,6 +981,35 @@ impl UnitManager { } } + #[allow(dead_code)] + pub(self) fn start_transient_unit( + &self, + properties: &[(&str, &str)], + name: &str, + job_mode_str: &str, + ) -> Result<()> { + let job_mode = JobMode::from_str(job_mode_str); + if let Err(e) = job_mode { + log::info!("Failed to parse job mode{}, err: {}", job_mode_str, e); + return Err(Error::InvalidData); + } + + let unit = self.bus.transient_unit_from_message(properties, name); + if let Err(e) = unit { + log::info!("Failed to get transient unit with err: {}", e); + return Err(e); + } + + let u = unit.unwrap(); + self.jm.exec( + &JobConf::new(&u, JobKind::Start), + job_mode.unwrap(), + &mut JobAffect::new(false), + )?; + + Ok(()) + } + fn get_unit_cgroup_path(&self, unit: Rc) -> String { let res = match unit.cg_path().to_str() { Some(res) => res.to_string(), @@ -1076,27 +1111,39 @@ impl UnitManager { state: Rc>, manager_config: Rc>, ) -> Rc { + let log_target = manager_config.borrow().LogTarget.clone(); + let log_file_size = manager_config.borrow().LogFileSize; + let log_file_num = manager_config.borrow().LogFileNumber; + let _rentry = Rc::new(UnitRe::new(relir)); let _db = Rc::new(UnitDb::new(&_rentry)); let _rt = Rc::new(UnitRT::new(relir, &_rentry, &_db)); + let _load = Rc::new(UnitLoad::new(dmr, &_rentry, &_db, &_rt, lookup_path)); let _jm = Rc::new(JobManager::new(eventr, relir, &_db, dmr)); + let _sms = Rc::new(UnitSubManagers::new( + relir, + &log_target, + log_file_size, + log_file_num, + )); let um = Rc::new(UnitManager { events: Rc::clone(eventr), reli: Rc::clone(relir), + state, rentry: Rc::clone(&_rentry), - load: UnitLoad::new(dmr, &_rentry, &_db, &_rt, lookup_path), db: Rc::clone(&_db), rt: Rc::clone(&_rt), + load: Rc::clone(&_load), jm: Rc::clone(&_jm), exec: ExecSpawn::new(), sigchld: Sigchld::new(eventr, relir, &_db, &_jm), notify: NotifyManager::new(eventr, relir, &_rentry, &_db, &_jm), - sms: UnitSubManagers::new(relir), - state, - manager_config, + sms: Rc::clone(&_sms), + bus: UnitBus::new(relir, &_load, &_jm, &_sms), }); um.load.set_um(&um); - um.sms.set_um(&um); + let umif = Rc::clone(&um); + um.sms.set_um(umif); um } @@ -1211,16 +1258,14 @@ impl UnitManager { fn remove_job_result(&self, _source: &str) {} - fn get_log_target(&self) -> String { - self.manager_config.borrow().LogTarget.clone() - } - - fn get_log_file_size(&self) -> u32 { - self.manager_config.borrow().LogFileSize - } + fn make_unit_consistent(&self, lunit: Option<&String>) { + if lunit.is_none() { + return; + } - fn get_log_file_number(&self) -> u32 { - self.manager_config.borrow().LogFileNumber + if let Some(unit) = self.db.units_get(lunit.unwrap()) { + unit.remove_transient(); + } } } @@ -1273,6 +1318,7 @@ impl ReStation for UnitManager { ReliLastFrame::CgEvent => todo!(), ReliLastFrame::Notify => self.notify.do_compensate_last(lframe, lunit), ReliLastFrame::SubManager => self.sms.do_compensate_last(lframe, lunit), + ReliLastFrame::CmdOp => self.make_unit_consistent(lunit), _ => {} // not concerned, do nothing }; } @@ -1380,152 +1426,6 @@ impl ReStation for UnitManager { fn into_unitobj(self: Box) -> Box; }*/ -mod unit_submanager { - use crate::unit::util::{self}; - - use super::UnitManager; - use core::rel::Reliability; - use core::unit::{UnitManagerObj, UnitType}; - use std::cell::RefCell; - use std::collections::HashMap; - use std::convert::TryFrom; - use std::rc::{Rc, Weak}; - - #[allow(dead_code)] - pub(super) struct UnitSubManagers { - reli: Rc, - um: RefCell>, - db: RefCell>>, - } - - impl UnitSubManagers { - pub(super) fn new(relir: &Rc) -> UnitSubManagers { - UnitSubManagers { - reli: Rc::clone(relir), - um: RefCell::new(Weak::new()), - db: RefCell::new(HashMap::new()), - } - } - - pub(super) fn set_um(&self, um: &Rc) { - // update um - self.um.replace(Rc::downgrade(um)); - - // fill all unit-types - for ut in 0..UnitType::UnitTypeMax as u32 { - self.add_sub(UnitType::try_from(ut).ok().unwrap()); - } - } - - pub(super) fn enumerate(&self) { - for (_, sub) in self.db.borrow().iter() { - sub.enumerate(); - } - } - - pub(super) fn input_rebuild(&self) { - for (_, sub) in self.db.borrow().iter() { - sub.input_rebuild(); - } - } - - pub(super) fn db_map(&self, reload: bool) { - for (_, sub) in self.db.borrow().iter() { - sub.db_map(reload); - } - } - - pub(super) fn db_insert(&self) { - for (_, sub) in self.db.borrow().iter() { - sub.db_insert(); - } - } - - pub(super) fn db_compensate_last( - &self, - lframe: (u32, Option, Option), - lunit: Option<&String>, - ) { - let utype = self.last_unittype(lframe); - if utype.is_none() || lunit.is_none() { - return; - } - - let unit_type = utype.unwrap(); - if let Some(sub) = self.db.borrow().get(&unit_type) { - sub.db_compensate_last(lframe, lunit); - } - } - - pub(super) fn do_compensate_last( - &self, - lframe: (u32, Option, Option), - lunit: Option<&String>, - ) { - let utype = self.last_unittype(lframe); - if utype.is_none() || lunit.is_none() { - return; - } - - let unit_type = utype.unwrap(); - if let Some(sub) = self.db.borrow().get(&unit_type) { - sub.do_compensate_last(lframe, lunit); - } - } - - fn add_sub(&self, unit_type: UnitType) { - assert!(!self.db.borrow().contains_key(&unit_type)); - - let sub = self.new_sub(unit_type); - if let Some(s) = sub { - self.db.borrow_mut().insert(unit_type, s); - } - } - - fn new_sub(&self, unit_type: UnitType) -> Option> { - let um = self.um(); - log::info!( - "Creating UnitManagerObj for {:?} by __um_obj_create()", - unit_type - ); - let sub = match util::create_um_obj( - unit_type, - &um.get_log_target(), - um.get_log_file_size(), - um.get_log_file_number(), - ) { - Err(_) => { - log::info!("__um_obj_create() of {:?} is not found", unit_type); - return None; - } - Ok(v) => v, - }; - - let reli = um.reliability(); - sub.attach_um(um); - sub.attach_reli(reli); - Some(sub) - } - - fn last_unittype(&self, lframe: (u32, Option, Option)) -> Option { - let (_, utype, _) = lframe; - utype?; - - let ut = utype.unwrap(); - if ut > UnitType::UnitTypeMax as u32 { - // error - return None; - } - - Some(UnitType::try_from(ut).ok().unwrap()) - } - - fn um(&self) -> Rc { - self.um.clone().into_inner().upgrade().unwrap() - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/core/sysmaster/src/unit/mod.rs b/core/sysmaster/src/unit/mod.rs index de98a5b7..3aea0c51 100644 --- a/core/sysmaster/src/unit/mod.rs +++ b/core/sysmaster/src/unit/mod.rs @@ -57,10 +57,11 @@ pub enum UnitErrno { // dependency: // rentry -> data -> base -> {util} -> -// entry -> {datastore -> runtime} -> job -> -// {execute | sigchld | notify} -> manager(uload) +// entry -> {datastore -> runtime} -> job -> submanager +// {execute | sigchld | notify} -> {bus -> manager(uload)} mod base; +mod bus; mod data; mod datastore; mod entry; @@ -70,6 +71,7 @@ mod notify; mod rentry; mod runtime; mod sigchld; +mod submanager; #[cfg(test)] mod test; mod uload; diff --git a/core/sysmaster/src/unit/rentry.rs b/core/sysmaster/src/unit/rentry.rs index f91247fa..a82d8add 100644 --- a/core/sysmaster/src/unit/rentry.rs +++ b/core/sysmaster/src/unit/rentry.rs @@ -18,6 +18,7 @@ use crate::manager::rentry::{ }; use crate::unit::entry::UnitEmergencyAction; use bitflags::bitflags; +use core::error::*; use core::rel::{ReDb, Reliability}; use core::serialize::DeserializeWith; use core::unit::{UnitRelations, UnitType}; @@ -62,11 +63,27 @@ pub enum UnitLoadState { #[derive(Clone, Debug, Serialize, Deserialize)] struct UnitReLoad { load_state: UnitLoadState, + transient: bool, + paths: Vec, + transient_file: Option, + last_section_private: i8, } impl UnitReLoad { - fn new(load_state: UnitLoadState) -> UnitReLoad { - UnitReLoad { load_state } + fn new( + load_state: UnitLoadState, + transient: bool, + paths: Vec, + transient_file: Option, + last_section_private: i8, + ) -> UnitReLoad { + UnitReLoad { + load_state, + transient, + paths, + transient_file, + last_section_private, + } } } @@ -231,6 +248,73 @@ pub(crate) struct UeConfigUnit { pub JobTimeoutAction: UnitEmergencyAction, } +impl UeConfigUnit { + pub(crate) fn set_property(&mut self, key: &str, value: &str) -> Result<()> { + match key { + "RefuseManualStart" => self.RefuseManualStart = basic::config::parse_boolean(value)?, + "RefuseManualStop" => self.RefuseManualStop = basic::config::parse_boolean(value)?, + "DefaultDependencies" => { + self.DefaultDependencies = basic::config::parse_boolean(value)? + } + "OnSuccessJobMode" => self.OnSuccessJobMode = JobMode::parse_from_str(value)?, + "OnFailureJobMode" => self.OnFailureJobMode = JobMode::parse_from_str(value)?, + "IgnoreOnIsolate" => self.IgnoreOnIsolate = basic::config::parse_boolean(value)?, + "JobTimeoutAction" => { + self.JobTimeoutAction = UnitEmergencyAction::parse_from_str(value)? + } + "StartLimitBurst" => self.StartLimitBurst = value.parse::()?, + "StartLimitAction" => { + self.StartLimitAction = UnitEmergencyAction::parse_from_str(value)? + } + "FailureAction" => self.FailureAction = UnitEmergencyAction::parse_from_str(value)?, + "SuccessAction" => self.SuccessAction = UnitEmergencyAction::parse_from_str(value)?, + "ConditionACPower" => { + self.ConditionACPower = Some(basic::config::parse_boolean(value)?) + } + "ConditionCapability" => self.ConditionCapability = value.to_string(), + "ConditionDirectoryNotEmpty" => self.ConditionDirectoryNotEmpty = value.to_string(), + "ConditionFileIsExecutable" => self.ConditionFileIsExecutable = value.to_string(), + "ConditionFileNotEmpty" => self.ConditionFileNotEmpty = value.to_string(), + "ConditionFirstBoot" => { + self.ConditionFirstBoot = Some(basic::config::parse_boolean(value)?) + } + "ConditionKernelCommandLine" => self.ConditionKernelCommandLine = value.to_string(), + "ConditionNeedsUpdate" => self.ConditionNeedsUpdate = value.to_string(), + "ConditionPathExists" => self.ConditionPathExists = value.to_string(), + "ConditionPathExistsGlob" => self.ConditionPathExistsGlob = value.to_string(), + "ConditionPathIsDirectory" => self.ConditionPathIsDirectory = value.to_string(), + "ConditionPathIsMountPoint" => self.ConditionPathIsMountPoint = value.to_string(), + "ConditionPathIsReadWrite" => self.ConditionPathIsReadWrite = value.to_string(), + "ConditionPathIsSymbolicLink" => self.ConditionPathIsSymbolicLink = value.to_string(), + "ConditionSecurity" => self.ConditionSecurity = value.to_string(), + "ConditionUser" => self.ConditionUser = value.to_string(), + "AssertPathExists" => self.AssertPathExists = value.to_string(), + "Documentation" => self.Documentation = value.to_string(), + "Wants" => self.Wants = vec_str_2_string(value), + "Requires" => self.Requires = vec_str_2_string(value), + "BindsTo" => self.BindsTo = vec_str_2_string(value), + "Requisite" => self.Requisite = vec_str_2_string(value), + "PartOf" => self.PartOf = vec_str_2_string(value), + "OnFailure" => self.OnFailure = vec_str_2_string(value), + "OnSuccess" => self.OnSuccess = vec_str_2_string(value), + "Before" => self.Before = vec_str_2_string(value), + "After" => self.After = vec_str_2_string(value), + "Conflicts" => self.Conflicts = vec_str_2_string(value), + "Description" => self.Description = value.to_string(), + _ => { + return Err(Error::NotFound { + what: "set property".to_string(), + }) + } + }; + Ok(()) + } +} + +fn vec_str_2_string(str: &str) -> Vec { + str.split_whitespace().map(|s| s.to_string()).collect() +} + #[derive(UnitSection, Default, Clone, Debug, Serialize, Deserialize)] pub struct UeConfigInstall { #[entry(append)] @@ -243,6 +327,15 @@ pub struct UeConfigInstall { pub Also: Vec, } +impl UeConfigInstall { + pub(crate) fn set_property(&mut self, _key: &str, _value: &str) -> Result<()> { + // nothing supported + Err(Error::NotFound { + what: "set unit install property".to_string(), + }) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] struct UnitReConfig { unit: UeConfigUnit, @@ -330,7 +423,7 @@ impl UmReNotify { pub(crate) struct UnitRe { // database: multi-instance(N) base: Rc>, // RELI_DB_HUNIT_BASE; key: unit_id, data: unit_type; - load: Rc>, // RELI_DB_HUNIT_LOAD; key: unit_id, data: load_state; + load: Rc>, // RELI_DB_HUNIT_LOAD; key: unit_id, data: load_state+transient+paths; conf: Rc>, // RELI_DB_HUNIT_CONFIG; key: unit_id, data: unit_conf+install_conf; cgroup: Rc>, // RELI_DB_HUNIT_CGROUP; key: unit_id, data: cg_path; child: Rc>, // RELI_DB_HUNIT_CHILD; key: unit_id, data: pid[s]; @@ -384,8 +477,22 @@ impl UnitRe { self.base.keys() } - pub(super) fn load_insert(&self, unit_id: &str, load_state: UnitLoadState) { - let u_load = UnitReLoad::new(load_state); + pub(super) fn load_insert( + &self, + unit_id: &str, + load_state: UnitLoadState, + transient: bool, + paths: Vec, + transient_file: Option, + last_section_private: i8, + ) { + let u_load = UnitReLoad::new( + load_state, + transient, + paths, + transient_file, + last_section_private, + ); self.load.insert(unit_id.to_owned(), u_load); } @@ -394,9 +501,21 @@ impl UnitRe { self.load.remove(&unit_id.to_string()); } - pub(super) fn load_get(&self, unit_id: &str) -> Option { + #[allow(clippy::type_complexity)] + pub(super) fn load_get( + &self, + unit_id: &str, + ) -> Option<(UnitLoadState, bool, Vec, Option, i8)> { let u_load = self.load.get(&unit_id.to_string()); - u_load.map(|l| l.load_state) + u_load.map(|l| { + ( + l.load_state, + l.transient, + l.paths, + l.transient_file, + l.last_section_private, + ) + }) } pub(super) fn conf_insert( diff --git a/core/sysmaster/src/unit/submanager.rs b/core/sysmaster/src/unit/submanager.rs new file mode 100644 index 00000000..3a4a0e01 --- /dev/null +++ b/core/sysmaster/src/unit/submanager.rs @@ -0,0 +1,179 @@ +// 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::unit::util::{self}; + +use core::rel::Reliability; +use core::unit::{UmIf, UnitManagerObj, UnitType}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::rc::{Rc, Weak}; + +#[allow(dead_code)] +pub(super) struct UnitSubManagers { + // associated objects + reli: Rc, + target: String, + file_size: u32, + file_number: u32, + + // owned objects + um: RefCell>>, + db: RefCell>>, +} + +impl UnitSubManagers { + pub(super) fn new( + relir: &Rc, + targetr: &str, + file_size: u32, + file_number: u32, + ) -> UnitSubManagers { + UnitSubManagers { + reli: Rc::clone(relir), + target: String::from(targetr), + file_size, + file_number, + um: RefCell::new(None), + db: RefCell::new(HashMap::new()), + } + } + + pub(super) fn set_um(&self, um: Rc) { + // update um + self.um.replace(Some(Rc::downgrade(&um))); + + // fill all unit-types + for ut in 0..UnitType::UnitTypeMax as u32 { + self.add_sub(UnitType::try_from(ut).ok().unwrap()); + } + } + + pub(super) fn enumerate(&self) { + for (_, sub) in self.db.borrow().iter() { + sub.enumerate(); + } + } + + pub(super) fn private_section(&self, unit_type: UnitType) -> String { + if let Some(sub) = self.db.borrow().get(&unit_type) { + sub.private_section(unit_type) + } else { + String::from("") + } + } + + pub(super) fn can_transient(&self, unit_type: UnitType) -> bool { + if let Some(sub) = self.db.borrow().get(&unit_type) { + sub.can_transient(unit_type) + } else { + false + } + } + + pub(super) fn input_rebuild(&self) { + for (_, sub) in self.db.borrow().iter() { + sub.input_rebuild(); + } + } + + pub(super) fn db_map(&self, reload: bool) { + for (_, sub) in self.db.borrow().iter() { + sub.db_map(reload); + } + } + + pub(super) fn db_insert(&self) { + for (_, sub) in self.db.borrow().iter() { + sub.db_insert(); + } + } + + pub(super) fn db_compensate_last( + &self, + lframe: (u32, Option, Option), + lunit: Option<&String>, + ) { + let utype = self.last_unittype(lframe); + if utype.is_none() || lunit.is_none() { + return; + } + + let unit_type = utype.unwrap(); + if let Some(sub) = self.db.borrow().get(&unit_type) { + sub.db_compensate_last(lframe, lunit); + } + } + + pub(super) fn do_compensate_last( + &self, + lframe: (u32, Option, Option), + lunit: Option<&String>, + ) { + let utype = self.last_unittype(lframe); + if utype.is_none() || lunit.is_none() { + return; + } + + let unit_type = utype.unwrap(); + if let Some(sub) = self.db.borrow().get(&unit_type) { + sub.do_compensate_last(lframe, lunit); + } + } + + fn add_sub(&self, unit_type: UnitType) { + assert!(!self.db.borrow().contains_key(&unit_type)); + + let sub = self.new_sub(unit_type); + if let Some(s) = sub { + self.db.borrow_mut().insert(unit_type, s); + } + } + + fn new_sub(&self, unit_type: UnitType) -> Option> { + let um = self.um(); + log::info!( + "Creating UnitManagerObj for {:?} by __um_obj_create()", + unit_type + ); + let sub = + match util::create_um_obj(unit_type, &self.target, self.file_size, self.file_number) { + Err(_) => { + log::info!("__um_obj_create() of {:?} is not found", unit_type); + return None; + } + Ok(v) => v, + }; + + sub.attach_um(um); + sub.attach_reli(Rc::clone(&self.reli)); + Some(sub) + } + + fn last_unittype(&self, lframe: (u32, Option, Option)) -> Option { + let (_, utype, _) = lframe; + utype?; + + let ut = utype.unwrap(); + if ut > UnitType::UnitTypeMax as u32 { + // error + return None; + } + + Some(UnitType::try_from(ut).ok().unwrap()) + } + + fn um(&self) -> Rc { + self.um.clone().into_inner().unwrap().upgrade().unwrap() + } +} diff --git a/core/sysmaster/src/unit/uload.rs b/core/sysmaster/src/unit/uload.rs index 6ae470f9..eff77c83 100644 --- a/core/sysmaster/src/unit/uload.rs +++ b/core/sysmaster/src/unit/uload.rs @@ -50,6 +50,10 @@ impl UnitLoad { self.data.load_unit(name) } + pub(super) fn load_update(&self, unit: &Rc) { + self.data.load_update(unit); + } + pub(super) fn set_um(&self, um: &Rc) { self.data.set_um(um); } @@ -153,6 +157,11 @@ impl UnitLoadData { Some(u) } + pub(self) fn load_update(&self, unit: &Rc) { + self.rt.push_load_queue(Rc::clone(unit)); + self.rt.dispatch_load_queue(); + } + pub(self) fn set_um(&self, um: &Rc) { self.um.replace(Rc::downgrade(um)); } diff --git a/core/sysmaster/src/unit/util/unit_om.rs b/core/sysmaster/src/unit/util/unit_om.rs index 476448c0..64842b3f 100644 --- a/core/sysmaster/src/unit/util/unit_om.rs +++ b/core/sysmaster/src/unit/util/unit_om.rs @@ -20,7 +20,7 @@ pub(crate) fn create_um_obj( pub(crate) fn create_subunit_with_um( unit_type: UnitType, um: Rc, -) -> Result> { +) -> Result> { #[cfg(feature = "noplugin")] return noplugin::create_subunit_with_um(unit_type, um); #[cfg(feature = "plugin")] @@ -47,7 +47,7 @@ mod plugin { pub(super) fn create_subunit_with_um( unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { Plugin::get_instance().create_subunit_with_um(unit_type, um) } } @@ -106,7 +106,7 @@ mod noplugin { pub(super) fn create_subunit_with_um( unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { let fun = match unit_type { #[cfg(feature = "mount")] UnitType::UnitMount => mount::__subunit_create_with_params, @@ -127,6 +127,6 @@ mod noplugin { } }; let boxed_raw = fun(um.clone()); - Ok(unsafe { Box::from_raw(boxed_raw) }) + Ok(unsafe { Rc::from_raw(boxed_raw) }) } } diff --git a/libs/basic/src/fs.rs b/libs/basic/src/fs.rs index 201173d6..11c4efb0 100644 --- a/libs/basic/src/fs.rs +++ b/libs/basic/src/fs.rs @@ -679,6 +679,8 @@ pub fn parse_pathbuf(s: &str) -> Result { Ok(PathBuf::from(path)) } +/// unit transient path in /run +pub const RUN_TRANSIENT_PATH: &str = "/run/sysmaster/transient"; /// unit lookup path in /etc pub const ETC_SYSTEM_PATH: &str = "/etc/sysmaster/system"; /// unit lookup path in /run @@ -721,6 +723,7 @@ impl LookupPaths { /// init lookup paths pub fn init_lookup_paths(&mut self) { + self.search_path.push(RUN_TRANSIENT_PATH.to_string()); self.search_path.push(ETC_SYSTEM_PATH.to_string()); self.search_path.push(RUN_SYSTEM_PATH.to_string()); self.search_path.push(LIB_SYSTEM_PATH.to_string()); @@ -897,6 +900,7 @@ mod tests { assert_eq!( lp.search_path, vec![ + "/run/sysmaster/transient", "/etc/sysmaster/system", "/run/sysmaster/system", "/usr/lib/sysmaster/system" -- Gitee From 3b5e0cf2fc4a5edd8706ced7172e306ec2e6da06 Mon Sep 17 00:00:00 2001 From: tanliyong Date: Tue, 9 Jan 2024 11:07:14 +0800 Subject: [PATCH 2/2] feature: support sysmaster-run --- Cargo.lock | 13 + Cargo.toml | 1 + ci/01-pre-commit.sh | 2 +- core/coms/path/Cargo.toml | 1 + core/coms/path/src/bus.rs | 140 +++++ core/coms/path/src/config.rs | 10 + core/coms/path/src/lib.rs | 1 + core/coms/path/src/manager.rs | 10 +- core/coms/path/src/rentry.rs | 52 +- core/coms/path/src/unit.rs | 22 +- core/coms/service/Cargo.toml | 1 + core/coms/service/src/bus.rs | 58 +- core/coms/service/src/rentry.rs | 39 +- core/coms/socket/Cargo.toml | 1 + core/coms/socket/src/bus.rs | 162 ++++++ core/coms/socket/src/config.rs | 10 + core/coms/socket/src/lib.rs | 1 + core/coms/socket/src/rentry.rs | 77 ++- core/coms/socket/src/unit.rs | 25 +- core/coms/timer/Cargo.toml | 3 +- core/coms/timer/src/bus.rs | 139 +++++ core/coms/timer/src/config.rs | 10 + core/coms/timer/src/lib.rs | 1 + core/coms/timer/src/manager.rs | 8 +- core/coms/timer/src/rentry.rs | 40 +- core/coms/timer/src/unit.rs | 12 + core/libcore/src/unit/base.rs | 6 +- core/libcore/src/unit/mod.rs | 6 +- core/sctl/src/main.rs | 4 +- core/sysmaster/src/manager/commands.rs | 23 +- core/sysmaster/src/manager/mod.rs | 11 + core/sysmaster/src/plugin/mod.rs | 6 +- core/sysmaster/src/unit/bus.rs | 21 +- core/sysmaster/src/unit/entry/uentry.rs | 12 +- core/sysmaster/src/unit/entry/unitx.rs | 4 +- core/sysmaster/src/unit/manager.rs | 40 +- core/sysmaster/src/unit/rentry.rs | 4 +- core/sysmaster/src/unit/util/unit_om.rs | 8 +- exts/run/Cargo.toml | 21 + exts/run/src/main.rs | 690 ++++++++++++++++++++++++ libs/basic/Cargo.toml | 2 + libs/basic/src/env.rs | 102 ++++ libs/basic/src/lib.rs | 2 + libs/cmdproto/src/proto/abi.proto | 18 + libs/cmdproto/src/proto/abi.rs | 34 +- libs/cmdproto/src/proto/execute.rs | 50 +- libs/cmdproto/src/proto/mod.rs | 15 + libs/constants/src/lib.rs | 2 +- 48 files changed, 1819 insertions(+), 101 deletions(-) create mode 100644 core/coms/path/src/bus.rs create mode 100644 core/coms/socket/src/bus.rs create mode 100644 core/coms/timer/src/bus.rs create mode 100644 exts/run/Cargo.toml create mode 100644 exts/run/src/main.rs create mode 100644 libs/basic/src/env.rs diff --git a/Cargo.lock b/Cargo.lock index 47074ee4..930b42dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1917,6 +1917,19 @@ dependencies = [ "nix 0.24.3", ] +[[package]] +name = "sysmaster-run" +version = "0.5.1" +dependencies = [ + "basic", + "clap", + "cmdproto", + "constants", + "core", + "log 0.5.1", + "nix 0.24.3", +] + [[package]] name = "sysmonitor" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 03a4e3e1..878591e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,7 @@ members = [ "exts/hwdb", "exts/machine-id-setup", # "exts/libudev", + "exts/run", #internal libraries crates "libs/cmdproto", #external libraries crates diff --git a/ci/01-pre-commit.sh b/ci/01-pre-commit.sh index 2c4c4a08..767cbf1b 100755 --- a/ci/01-pre-commit.sh +++ b/ci/01-pre-commit.sh @@ -28,7 +28,7 @@ cargo check || exit 1 for rustlist in `git diff origin/master --name-only | grep \.rs$ | grep -v "/examples/" | tr '\n' ' '` do # Allow libblkid/mod.rs and input_event_codes_rs to use, because they are auto generated. - if [[ $rustlist =~ "libblkid/mod.rs" ]] || [[ $rustlist =~ "input_event_codes_rs" ]]; then + if [[ $rustlist =~ "libblkid/mod.rs" ]] || [[ $rustlist =~ "input_event_codes_rs" ]]|| [[ $rustlist =~ "proto/abi.rs" ]]; then continue fi # do not use global #!allow, exclude non_snake_case diff --git a/core/coms/path/Cargo.toml b/core/coms/path/Cargo.toml index 9a257345..45b4189a 100644 --- a/core/coms/path/Cargo.toml +++ b/core/coms/path/Cargo.toml @@ -13,6 +13,7 @@ name = "path" basic = { path = "../../../libs/basic", default-features = false, features = [ "mount", "glob", + "config", ] } core = { path = "../../libcore", default-features = false } event = { path = "../../../libs/event" } diff --git a/core/coms/path/src/bus.rs b/core/coms/path/src/bus.rs new file mode 100644 index 00000000..12d0fbf3 --- /dev/null +++ b/core/coms/path/src/bus.rs @@ -0,0 +1,140 @@ +// 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 super::comm::PathUnitComm; +use super::config::PathConfig; +use core::error::*; +use core::unit::{self, UnitWriteFlags}; +use std::rc::Rc; + +pub struct PathBus { + // associated objects + comm: Rc, + // owned objects + config: Rc, +} + +impl PathBus { + pub(super) fn new(commr: &Rc, configr: &Rc) -> PathBus { + PathBus { + comm: Rc::clone(commr), + config: Rc::clone(configr), + } + } + + pub(super) fn unit_set_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let mut ret = self.cgroup_set_transient_property(key, value, flags); + if let Err(Error::NotFound { what: _ }) = ret { + let unit = self.comm.owner().unwrap(); + if unit.transient() && unit.is_load_stub() { + ret = self.unit_set_transient_property(key, value, flags); + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.exec_set_transient_property(key, value, flags); + } + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.kill_set_transient_property(key, value, flags); + } + } + } + + ret + } + + fn unit_set_transient_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let real_flags = flags | UnitWriteFlags::PRIVATE; + match key { + "Unit" + | "MakeDirectory" + | "DirectoryMode" + | "TriggerLimitBurst" + | "TriggerLimitIntervalSec" => self.unit_write_property(key, value, real_flags, false), + "PathExists" | "PathExistsGlob" | "PathChanged" | "PathModified" + | "DirectoryNotEmpty" => self.unit_write_property(key, value, real_flags, false), + str_key => Err(Error::NotFound { + what: format!("set transient property:{}", str_key), + }), + } + } + + fn exec_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set exec property:{}", key), + }) + } + + fn kill_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set kill property:{}", key), + }) + } + + fn cgroup_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set cgroup property:{}", key), + }) + } + + fn unit_write_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + update: bool, + ) -> Result<()> { + if unit::unit_write_flags_is_noop(flags) { + return Ok(()); + } + + let unit = self.comm.owner().unwrap(); + let um = self.comm.um(); + let ps = um.private_section(unit.unit_type()); + + if update { + self.set_property(key, value)?; + } + unit::unit_write_settingf(unit, &ps, flags, key, format_args!("{}={}", key, value)) + } + + fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.config.set_property(key, value) + } +} diff --git a/core/coms/path/src/config.rs b/core/coms/path/src/config.rs index 70f6eb85..1473db60 100644 --- a/core/coms/path/src/config.rs +++ b/core/coms/path/src/config.rs @@ -30,6 +30,10 @@ impl PathConfigData { pub(self) fn new(Path: SectionPath) -> PathConfigData { PathConfigData { Path } } + + pub(self) fn set_property(&mut self, key: &str, value: &str) -> Result<(), core::error::Error> { + self.Path.set_property(key, value) + } } pub(super) struct PathConfig { @@ -94,4 +98,10 @@ impl PathConfig { pub(super) fn config_data(&self) -> Rc> { self.data.clone() } + + pub(super) fn set_property(&self, key: &str, value: &str) -> Result<()> { + let ret = self.data.borrow_mut().set_property(key, value); + self.db_update(); + ret + } } diff --git a/core/coms/path/src/lib.rs b/core/coms/path/src/lib.rs index 6e6f1f2a..c26baf8e 100644 --- a/core/coms/path/src/lib.rs +++ b/core/coms/path/src/lib.rs @@ -43,6 +43,7 @@ pub use {manager::__um_obj_create, unit::__subunit_create_with_params}; // base -> rentry -> {comm | config} // mng -> unit -> manager mod base; +mod bus; mod comm; mod config; mod manager; diff --git a/core/coms/path/src/manager.rs b/core/coms/path/src/manager.rs index 92bdeefa..568f0a08 100644 --- a/core/coms/path/src/manager.rs +++ b/core/coms/path/src/manager.rs @@ -17,7 +17,7 @@ use constants::LOG_FILE_PATH; use super::comm::PathUmComm; use core::rel::{ReStation, Reliability}; -use core::unit::{UmIf, UnitManagerObj, UnitMngUtil}; +use core::unit::{UmIf, UnitManagerObj, UnitMngUtil, UnitType}; use std::rc::Rc; use std::sync::Arc; struct PathManager { @@ -35,7 +35,13 @@ impl PathManager { } impl UnitManagerObj for PathManager { - // nothing to customize + fn private_section(&self, _unit_type: UnitType) -> String { + "Path".into() + } + + fn can_transient(&self, _unit_type: UnitType) -> bool { + true + } } impl ReStation for PathManager { diff --git a/core/coms/path/src/rentry.rs b/core/coms/path/src/rentry.rs index 04d8b4d1..794ae130 100644 --- a/core/coms/path/src/rentry.rs +++ b/core/coms/path/src/rentry.rs @@ -11,10 +11,12 @@ // See the Mulan PSL v2 for more details. // #![allow(non_snake_case)] +use basic::config::parse_boolean; use basic::time::USEC_INFINITY; use core::exec::parse_mode; use core::rel::{ReDb, ReDbRwTxn, ReDbTable, ReliSwitch, Reliability}; use core::unit::PathType; +use core::Error; use macros::{EnumDisplay, UnitSection}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -25,15 +27,15 @@ const RELI_DB_HPATH_MNG: &str = "pathmng"; #[derive(UnitSection, Serialize, Deserialize, Debug, Default, Clone)] pub struct SectionPath { - #[entry(append)] + #[entry(append, parser = parse_pathbuf_vec)] pub PathExists: Vec, - #[entry(append)] + #[entry(append, parser = parse_pathbuf_vec)] pub PathExistsGlob: Vec, - #[entry(append)] + #[entry(append, parser = parse_pathbuf_vec)] pub PathChanged: Vec, - #[entry(append)] + #[entry(append, parser = parse_pathbuf_vec)] pub PathModified: Vec, - #[entry(append)] + #[entry(append, parser = parse_pathbuf_vec)] pub DirectoryNotEmpty: Vec, #[entry(default = String::new())] pub Unit: String, @@ -51,6 +53,46 @@ pub struct SectionPath { pub TriggerLimitBurst: u32, } +fn parse_pathbuf_vec(s: &str) -> Result, core::error::Error> { + let mut res = Vec::new(); + for v in s.split_ascii_whitespace() { + let path = + basic::fs::parse_absolute_path(v).map_err(|_| core::error::Error::ConfigureError { + msg: "Invalid PathBuf".to_string(), + })?; + res.push(PathBuf::from(path)); + } + Ok(res) +} + +impl SectionPath { + pub(super) fn set_property( + &mut self, + key: &str, + value: &str, + ) -> Result<(), core::error::Error> { + match key { + "Unit" => self.Unit = value.to_string(), + "MakeDirectory" => self.MakeDirectory = parse_boolean(value)?, + "DirectoryMode" => self.DirectoryMode = parse_mode(value)?, + "TriggerLimitBurst" => self.TriggerLimitBurst = value.parse::()?, + "TriggerLimitIntervalSec" => self.TriggerLimitIntervalSec = value.parse::()?, + //Paths + "PathExists" => self.PathExists = parse_pathbuf_vec(value)?, + "PathExistsGlob" => self.PathExistsGlob = parse_pathbuf_vec(value)?, + "PathChanged" => self.PathChanged = parse_pathbuf_vec(value)?, + "PathModified" => self.PathModified = parse_pathbuf_vec(value)?, + "DirectoryNotEmpty" => self.DirectoryNotEmpty = parse_pathbuf_vec(value)?, + str_key => { + return Err(Error::NotFound { + what: format!("set timer property:{}", str_key), + }); + } + } + Ok(()) + } +} + #[derive(PartialEq, Eq, Debug, Copy, Clone, Serialize, Deserialize, EnumDisplay)] pub(crate) enum PathState { Dead, diff --git a/core/coms/path/src/unit.rs b/core/coms/path/src/unit.rs index c466e345..cecbfa70 100644 --- a/core/coms/path/src/unit.rs +++ b/core/coms/path/src/unit.rs @@ -15,6 +15,7 @@ use super::comm::PathUnitComm; use super::mng::PathMng; +use crate::bus::PathBus; use crate::config::PathConfig; use crate::mng::PathInotify; use crate::rentry::PathState; @@ -37,6 +38,7 @@ struct PathUnit { comm: Rc, mng: Rc, config: Rc, + bus: PathBus, } impl ReStation for PathUnit { @@ -70,12 +72,13 @@ impl ReStation for PathUnit { impl PathUnit { fn new(_um: Rc) -> PathUnit { - let _comm = Rc::new(PathUnitComm::new()); - let _config = Rc::new(PathConfig::new(&_comm)); + let comm = Rc::new(PathUnitComm::new()); + let config = Rc::new(PathConfig::new(&comm)); PathUnit { - comm: Rc::clone(&_comm), - mng: Rc::new(PathMng::new(&_comm, &_config)), - config: Rc::clone(&_config), + comm: Rc::clone(&comm), + mng: Rc::new(PathMng::new(&comm, &config)), + config: Rc::clone(&config), + bus: PathBus::new(&comm, &config), } } @@ -350,6 +353,15 @@ impl SubUnit for PathUnit { fn reset_failed(&self) { self.mng.reset_failed() } + + fn unit_set_property( + &self, + key: &str, + value: &str, + flags: core::unit::UnitWriteFlags, + ) -> Result<()> { + self.bus.unit_set_property(key, value, flags) + } } impl UnitMngUtil for PathUnit { diff --git a/core/coms/service/Cargo.toml b/core/coms/service/Cargo.toml index bc5957d8..c6e596ca 100644 --- a/core/coms/service/Cargo.toml +++ b/core/coms/service/Cargo.toml @@ -16,6 +16,7 @@ basic = { path = "../../../libs/basic", default-features = false, features = [ "fd", "unit_name", "time", + "config", ] } cgroup = { path = "../../../libs/cgroup", default-features = false } constants = { path = "../../../libs/constants" } diff --git a/core/coms/service/src/bus.rs b/core/coms/service/src/bus.rs index 0028ca50..c96997b2 100644 --- a/core/coms/service/src/bus.rs +++ b/core/coms/service/src/bus.rs @@ -74,45 +74,67 @@ impl ServiceBus { | "ExecReload" | "ExecCondition" => { self.unit_write_property(key, value, real_flags, false) } - _ => Err(Error::NotFound { - what: "set transient property".to_string(), + str_key => Err(Error::NotFound { + what: format!("set transient property:{}", str_key), }), } } fn exec_set_transient_property( &self, - _key: &str, - _value: &str, - _flags: UnitWriteFlags, + key: &str, + value: &str, + flags: UnitWriteFlags, ) -> Result<()> { - // not supported now - Err(Error::NotFound { - what: "set cgroup property".to_string(), - }) + let real_flags = flags | UnitWriteFlags::PRIVATE; + match key { + "User" + | "Group" + | "RootDirectory" + | "NonBlocking" + | "RuntimeDirectoryPreserve" + | "UMask" + | "SELinuxContext" + | "WorkingDirectory" + | "Environment" + | "EnvironmentFile" + | "RuntimeDirectory" + | "StateDirectory" + | "ExecReload" + | "ExecCondition" => self.unit_write_property(key, value, real_flags, false), + "LimitCORE" | "LimitNOFILE" | "LimitNPROC" => { + self.unit_write_property(key, value, real_flags, false) + } + str_key => Err(Error::NotFound { + what: format!("set exec property:{}", str_key), + }), + } } fn kill_set_transient_property( &self, - _key: &str, - _value: &str, - _flags: UnitWriteFlags, + key: &str, + value: &str, + flags: UnitWriteFlags, ) -> Result<()> { - // not supported now - Err(Error::NotFound { - what: "set cgroup property".to_string(), - }) + let real_flags = flags | UnitWriteFlags::PRIVATE; + match key { + "KillMode" | "KillSignal" => self.unit_write_property(key, value, real_flags, false), + str_key => Err(Error::NotFound { + what: format!("set kill property:{}", str_key), + }), + } } fn cgroup_set_transient_property( &self, - _key: &str, + key: &str, _value: &str, _flags: UnitWriteFlags, ) -> Result<()> { // not supported now Err(Error::NotFound { - what: "set cgroup property".to_string(), + what: format!("set cgroup property:{}", key), }) } diff --git a/core/coms/service/src/rentry.rs b/core/coms/service/src/rentry.rs index 101c9a12..8022e20d 100644 --- a/core/coms/service/src/rentry.rs +++ b/core/coms/service/src/rentry.rs @@ -13,6 +13,7 @@ #![allow(non_snake_case)] use crate::monitor::ServiceMonitor; +use basic::fs::parse_pathbuf; use basic::fs::{path_is_abosolute, path_length_is_valid, path_name_is_safe, path_simplify}; use core::exec::PreserveMode; use macros::{EnumDisplay, UnitSection}; @@ -339,6 +340,10 @@ impl SectionService { match key { "RemainAfterExit" => self.RemainAfterExit = basic::config::parse_boolean(value)?, "Type" => self.Type = ServiceType::parse_from_str(value)?, + "RestartSec" => self.RestartSec = value.parse::()?, + "TimeoutSec" => self.TimeoutSec = parse_timeout(value)?, + "TimeoutStartSec" => self.TimeoutStartSec = parse_timeout(value)?, + "TimeoutStopSec" => self.TimeoutStopSec = parse_timeout(value)?, "NotifyAccess" => self.NotifyAccess = Some(NotifyAccess::parse_from_str(value)?), "PIDFile" => self.PIDFile = Some(parse_pidfile(value)?), "Restart" => self.Restart = ServiceRestart::parse_from_str(value)?, @@ -352,9 +357,39 @@ impl SectionService { "ExecStopPost" => self.ExecStopPost = core::exec::parse_exec_command(value)?, "ExecReload" => self.ExecReload = core::exec::parse_exec_command(value)?, "ExecCondition" => self.ExecCondition = core::exec::parse_exec_command(value)?, - _ => { + //exec context + "User" => self.User = value.to_string(), + "Group" => self.User = value.to_string(), + "RootDirectory" => self.RootDirectory = Some(parse_pathbuf(value)?), + "NonBlocking" => self.NonBlocking = basic::config::parse_boolean(value)?, + "RuntimeDirectoryPreserve" => { + self.RuntimeDirectoryPreserve = PreserveMode::parse_from_str(value)? + } + "UMask" => self.UMask = value.to_string(), + "SELinuxContext" => self.SELinuxContext = Some(value.to_string()), + "WorkingDirectory" => { + self.WorkingDirectory = core::exec::parse_working_directory(value)? + } + "Environment" => self.Environment = Some(core::exec::parse_environment(value)?), + "EnvironmentFile" => { + self.EnvironmentFile = value + .split_whitespace() + .map(|str| str.to_string()) + .collect() + } + "RuntimeDirectory" => { + self.RuntimeDirectory = core::exec::parse_runtime_directory(value)? + } + "StateDirectory" => self.StateDirectory = core::exec::parse_state_directory(value)?, + "LimitCORE" => self.LimitCORE = Some(Rlimit::parse_from_str(value)?), + "LimitNOFILE" => self.LimitNOFILE = Some(Rlimit::parse_from_str(value)?), + "LimitNPROC" => self.LimitNPROC = Some(Rlimit::parse_from_str(value)?), + //kill context + "KillMode" => self.KillMode = KillMode::parse_from_str(value)?, + "KillSignal" => self.KillSignal = value.to_string(), + str_key => { return Err(Error::NotFound { - what: "set property".to_string(), + what: format!("set property:{}", str_key), }) } }; diff --git a/core/coms/socket/Cargo.toml b/core/coms/socket/Cargo.toml index 6ac97a67..b826dba9 100644 --- a/core/coms/socket/Cargo.toml +++ b/core/coms/socket/Cargo.toml @@ -14,6 +14,7 @@ basic = { path = "../../../libs/basic", default-features = false, features = [ "io", "fd", "socket", + "config", ] } constants = { path = "../../../libs/constants" } core = { path = "../../libcore", default-features = false } diff --git a/core/coms/socket/src/bus.rs b/core/coms/socket/src/bus.rs new file mode 100644 index 00000000..ac98c396 --- /dev/null +++ b/core/coms/socket/src/bus.rs @@ -0,0 +1,162 @@ +// 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 super::comm::SocketUnitComm; +use super::config::SocketConfig; +use core::error::*; +use core::unit::{self, UnitWriteFlags}; +use std::rc::Rc; + +pub struct SocketBus { + // associated objects + comm: Rc, + config: Rc, + // owned objects +} + +impl SocketBus { + pub(super) fn new(commr: &Rc, configr: &Rc) -> SocketBus { + SocketBus { + comm: Rc::clone(commr), + config: Rc::clone(configr), + } + } + + pub(super) fn unit_set_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let mut ret = self.cgroup_set_transient_property(key, value, flags); + if let Err(Error::NotFound { what: _ }) = ret { + let unit = self.comm.owner().unwrap(); + if unit.transient() && unit.is_load_stub() { + ret = self.unit_set_transient_property(key, value, flags); + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.exec_set_transient_property(key, value, flags); + } + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.kill_set_transient_property(key, value, flags); + } + } + } + + ret + } + + fn unit_set_transient_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let real_flags = flags | UnitWriteFlags::PRIVATE; + match key { + "Accept" + | "FlushPending" + | "KeepAlive" + | "Broadcast" + | "PassCredentials" + | "PassPacketInfo" + | "PassSecurity" + | "RemoveOnStop" + | "KeepAliveProbes" + | "SocketMode" + | "KeepAliveTimeSec" + | "KeepAliveIntervalSec" + | "SocketUser" + | "SocketGroup" + | "ReceiveBuffer" + | "SendBuffer" + | "Symlinks" + | "ListenStream" + | "ListenDatagram" + | "ListenNetlink" + | "ListenSequentialPacket" + | "ListenFIFO" + | "ListenSpecial" => self.unit_write_property(key, value, real_flags, false), + "ExecStartPre" | "ExecStartPost" | "ExecStopPre" | "ExecStopPost" => { + self.unit_write_property(key, value, real_flags, false) + } + str_key => Err(Error::NotFound { + what: format!("set transient property:{}", str_key), + }), + } + } + + fn exec_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set exec property:{}", key), + }) + } + + fn kill_set_transient_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let real_flags = flags | UnitWriteFlags::PRIVATE; + match key { + "KillMode" | "KillSignal" => self.unit_write_property(key, value, real_flags, false), + str_key => Err(Error::NotFound { + what: format!("set kill property:{}", str_key), + }), + } + } + + fn cgroup_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set cgroup property:{}", key), + }) + } + + fn unit_write_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + update: bool, + ) -> Result<()> { + if unit::unit_write_flags_is_noop(flags) { + return Ok(()); + } + + let unit = self.comm.owner().unwrap(); + let um = self.comm.um(); + let ps = um.private_section(unit.unit_type()); + + if update { + self.set_property(key, value)?; + } + unit::unit_write_settingf(unit, &ps, flags, key, format_args!("{}={}", key, value)) + } + + fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.config.set_property(key, value) + } +} diff --git a/core/coms/socket/src/config.rs b/core/coms/socket/src/config.rs index 011b5a56..99a72ae3 100644 --- a/core/coms/socket/src/config.rs +++ b/core/coms/socket/src/config.rs @@ -287,6 +287,12 @@ impl SocketConfig { self.kill_context.set_kill_signal(signal); Ok(()) } + + pub(super) fn set_property(&self, key: &str, value: &str) -> Result<()> { + let ret = self.data.borrow_mut().set_property(key, value); + self.db_update(); + ret + } } #[derive(PartialEq)] @@ -320,6 +326,10 @@ impl SocketConfigData { } Some(res) } + + pub(self) fn set_property(&mut self, key: &str, value: &str) -> Result<()> { + self.Socket.set_property(key, value) + } } pub(super) struct SocketPortConf { diff --git a/core/coms/socket/src/lib.rs b/core/coms/socket/src/lib.rs index 8b4e2cce..aa2b31f0 100644 --- a/core/coms/socket/src/lib.rs +++ b/core/coms/socket/src/lib.rs @@ -88,6 +88,7 @@ pub use {manager::__um_obj_create, unit::__subunit_create_with_params}; // {socket_mng | socket_load} -> socket_unit -> socket_manager mod base; +mod bus; mod comm; mod config; mod load; diff --git a/core/coms/socket/src/rentry.rs b/core/coms/socket/src/rentry.rs index 98575aed..b5912246 100644 --- a/core/coms/socket/src/rentry.rs +++ b/core/coms/socket/src/rentry.rs @@ -11,16 +11,17 @@ // See the Mulan PSL v2 for more details. // #![allow(non_snake_case)] -use core::exec::ExecCommand; +use core::exec::{parse_mode, ExecCommand}; use core::rel::{ReDb, ReDbRwTxn, ReDbTable, ReliSwitch, Reliability}; use core::unit::KillMode; +use core::Error; use macros::EnumDisplay; use nix::unistd::Pid; use serde::{Deserialize, Serialize}; use std::os::unix::prelude::RawFd; use std::path::PathBuf; use std::rc::Rc; -use unit_parser::prelude::UnitSection; +use unit_parser::prelude::{UnitEntry, UnitSection}; struct SocketReDb(ReDb); @@ -105,6 +106,78 @@ pub(super) struct SectionSocket { pub KillSignal: String, } +impl SectionSocket { + pub(super) fn set_property( + &mut self, + key: &str, + value: &str, + ) -> Result<(), core::error::Error> { + match key { + "ExecStartPre" => self.ExecStartPre = core::exec::parse_exec_command(value)?, + "ExecStartPost" => self.ExecStartPost = core::exec::parse_exec_command(value)?, + "ExecStopPre" => self.ExecStopPre = core::exec::parse_exec_command(value)?, + "ExecStopPost" => self.ExecStopPost = core::exec::parse_exec_command(value)?, + "ListenStream" => { + self.ListenStream = value + .split_whitespace() + .map(|str| str.to_string()) + .collect() + } + "ListenDatagram" => { + self.ListenDatagram = value + .split_whitespace() + .map(|str| str.to_string()) + .collect() + } + "ListenNetlink" => self.ListenNetlink = deserialize_netlink_vec(value)?, + "ListenSequentialPacket" => { + self.ListenSequentialPacket = value + .split_whitespace() + .map(|str| str.to_string()) + .collect() + } + "ListenFIFO" => { + self.ListenFIFO = value + .split_whitespace() + .map(|str| str.to_string()) + .collect() + } + "ListenSpecial" => { + self.ListenSpecial = value + .split_whitespace() + .map(|str| str.to_string()) + .collect() + } + "Accept" => self.Accept = basic::config::parse_boolean(value)?, + "FlushPending" => self.FlushPending = basic::config::parse_boolean(value)?, + "ReceiveBuffer" => self.ReceiveBuffer = Some(value.parse::()?), + "SendBuffer" => self.SendBuffer = Some(value.parse::()?), + "PassCredentials" => self.PassCredentials = Some(basic::config::parse_boolean(value)?), + "PassPacketInfo" => self.PassPacketInfo = Some(basic::config::parse_boolean(value)?), + "KeepAlive" => self.KeepAlive = Some(basic::config::parse_boolean(value)?), + "KeepAliveTimeSec" => self.KeepAliveTimeSec = Some(value.parse::()?), + "KeepAliveIntervalSec" => self.KeepAliveIntervalSec = Some(value.parse::()?), + "KeepAliveProbes" => self.KeepAliveProbes = Some(value.parse::()?), + "Broadcast" => self.Broadcast = Some(basic::config::parse_boolean(value)?), + "RemoveOnStop" => self.RemoveOnStop = basic::config::parse_boolean(value)?, + "Symlinks" => self.Symlinks = parse_pathbuf_vec(value)?, + "PassSecurity" => self.PassSecurity = Some(basic::config::parse_boolean(value)?), + "SocketMode" => self.SocketMode = parse_mode(value)?, + "SocketUser" => self.SocketUser = value.to_string(), + "SocketGroup" => self.SocketGroup = value.to_string(), + //kill context + "KillMode" => self.KillMode = KillMode::parse_from_str(value)?, + "KillSignal" => self.KillSignal = value.to_string(), + str_key => { + return Err(Error::NotFound { + what: format!("set property:{}", str_key), + }); + } + } + Ok(()) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] struct SocketReConf { socket: SectionSocket, diff --git a/core/coms/socket/src/unit.rs b/core/coms/socket/src/unit.rs index f4834f84..7ed75dc3 100644 --- a/core/coms/socket/src/unit.rs +++ b/core/coms/socket/src/unit.rs @@ -15,6 +15,7 @@ //! Trait UnitMngUtil is used to attach the Unitmanager to the sub unit. //! Trait UnitSubClass implement the convert from sub unit to UnitObj. +use crate::bus::SocketBus; use crate::mng::SocketMngPort; use crate::port::SocketPort; use crate::rentry::SocketState; @@ -22,7 +23,7 @@ use crate::{comm::SocketUnitComm, config::SocketConfig, load::SocketLoad, mng::S use core::error::*; use core::exec::ExecContext; use core::rel::{ReStation, Reliability}; -use core::unit::{SubUnit, UmIf, UnitActiveState, UnitBase, UnitMngUtil}; +use core::unit::{SubUnit, UmIf, UnitActiveState, UnitBase, UnitMngUtil, UnitWriteFlags}; use nix::sys::wait::WaitStatus; use std::any::Any; use std::{path::PathBuf, rc::Rc}; @@ -33,6 +34,7 @@ struct SocketUnit { config: Rc, mng: Rc, load: SocketLoad, + bus: SocketBus, } impl ReStation for SocketUnit { @@ -162,6 +164,10 @@ impl SubUnit for SocketUnit { self.comm.attach_unit(unit); self.db_insert(); } + + fn unit_set_property(&self, key: &str, value: &str, flags: UnitWriteFlags) -> Result<()> { + self.bus.unit_set_property(key, value, flags) + } } // attach the UnitManager for weak reference @@ -178,13 +184,18 @@ impl UnitMngUtil for SocketUnit { impl SocketUnit { fn new(_um: Rc) -> SocketUnit { let context = Rc::new(ExecContext::new()); - let _comm = Rc::new(SocketUnitComm::new()); - let _config = Rc::new(SocketConfig::new(&_comm)); + let comm = Rc::new(SocketUnitComm::new()); + let config = Rc::new(SocketConfig::new(&comm)); SocketUnit { - comm: Rc::clone(&_comm), - config: Rc::clone(&_config), - mng: Rc::new(SocketMng::new(&_comm, &_config, &context)), - load: SocketLoad::new(&_config, &_comm), + comm: Rc::clone(&comm), + config: Rc::clone(&config), + mng: Rc::new(SocketMng::new( + &Rc::clone(&comm), + &Rc::clone(&config), + &Rc::clone(&context), + )), + load: SocketLoad::new(&Rc::clone(&config), &Rc::clone(&comm)), + bus: SocketBus::new(&comm, &config), } } diff --git a/core/coms/timer/Cargo.toml b/core/coms/timer/Cargo.toml index 3244dff3..b5f8b693 100644 --- a/core/coms/timer/Cargo.toml +++ b/core/coms/timer/Cargo.toml @@ -7,7 +7,8 @@ edition = "2021" [dependencies] basic = { path = "../../../libs/basic", default-features = false, features = [ - "machine" + "machine", + "config", ] } log = { path = "../../../libs/log" } diff --git a/core/coms/timer/src/bus.rs b/core/coms/timer/src/bus.rs new file mode 100644 index 00000000..6528d093 --- /dev/null +++ b/core/coms/timer/src/bus.rs @@ -0,0 +1,139 @@ +// 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 super::comm::TimerUnitComm; +use super::config::TimerConfig; +use core::error::*; +use core::unit::{self, UnitWriteFlags}; +use std::rc::Rc; + +pub struct TimerBus { + // associated objects + comm: Rc, + config: Rc, + // owned objects +} + +impl TimerBus { + pub(super) fn new(commr: &Rc, configr: &Rc) -> TimerBus { + TimerBus { + comm: Rc::clone(commr), + config: Rc::clone(configr), + } + } + + pub(super) fn unit_set_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let mut ret = self.cgroup_set_transient_property(key, value, flags); + if let Err(Error::NotFound { what: _ }) = ret { + let unit = self.comm.owner().unwrap(); + if unit.transient() && unit.is_load_stub() { + ret = self.unit_set_transient_property(key, value, flags); + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.exec_set_transient_property(key, value, flags); + } + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.kill_set_transient_property(key, value, flags); + } + } + } + + ret + } + + fn unit_set_transient_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let real_flags = flags | UnitWriteFlags::PRIVATE; + match key { + "AccuracySec" | "RandomizedDelaySec" | "WakeSystem" | "Persistent" + | "RemainAfterElapse" | "OnActiveSec" | "OnBootSec" | "OnStartupSec" + | "OnUnitActiveSec" | "OnUnitInactiveSec" | "OnCalendar" => { + self.unit_write_property(key, value, real_flags, false) + } + "Unit" => self.unit_write_property(key, value, real_flags, false), + str_key => Err(Error::NotFound { + what: format!("set transient property:{}", str_key), + }), + } + } + + fn exec_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set exec property:{}", key), + }) + } + + fn kill_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set kill property:{}", key), + }) + } + + fn cgroup_set_transient_property( + &self, + key: &str, + _value: &str, + _flags: UnitWriteFlags, + ) -> Result<()> { + // not supported now + Err(Error::NotFound { + what: format!("set cgroup property:{}", key), + }) + } + + fn unit_write_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + update: bool, + ) -> Result<()> { + if unit::unit_write_flags_is_noop(flags) { + return Ok(()); + } + + let unit = self.comm.owner().unwrap(); + let um = self.comm.um(); + let ps = um.private_section(unit.unit_type()); + + if update { + self.set_property(key, value)?; + } + unit::unit_write_settingf(unit, &ps, flags, key, format_args!("{}={}", key, value)) + } + + fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.config.set_property(key, value) + } +} diff --git a/core/coms/timer/src/config.rs b/core/coms/timer/src/config.rs index ad0b42dd..c6e7e205 100644 --- a/core/coms/timer/src/config.rs +++ b/core/coms/timer/src/config.rs @@ -58,6 +58,10 @@ impl TimerConfigData { pub(self) fn new(Timer: SectionTimer) -> TimerConfigData { TimerConfigData { Timer } } + + pub(self) fn set_property(&mut self, key: &str, value: &str) -> Result<(), core::error::Error> { + self.Timer.set_property(key, value) + } } pub struct TimerConfig { @@ -243,4 +247,10 @@ impl TimerConfig { self.timerunitref.borrow_mut().set_ref(u.id(), target) }; } + + pub(super) fn set_property(&self, key: &str, value: &str) -> Result<(), core::error::Error> { + let ret = self.data.borrow_mut().set_property(key, value); + self.db_update(); + ret + } } diff --git a/core/coms/timer/src/lib.rs b/core/coms/timer/src/lib.rs index f4fb969a..9c833ebd 100644 --- a/core/coms/timer/src/lib.rs +++ b/core/coms/timer/src/lib.rs @@ -63,6 +63,7 @@ pub use {manager::__um_obj_create, unit::__subunit_create_with_params}; // timer_mng -> timer_unit -> timer_manager mod base; +mod bus; mod comm; mod config; mod load; diff --git a/core/coms/timer/src/manager.rs b/core/coms/timer/src/manager.rs index 6d87c346..ef116f84 100644 --- a/core/coms/timer/src/manager.rs +++ b/core/coms/timer/src/manager.rs @@ -35,7 +35,13 @@ impl TimerManager { } impl UnitManagerObj for TimerManager { - // nothing to customize + fn private_section(&self, _unit_type: core::unit::UnitType) -> String { + "Timer".into() + } + + fn can_transient(&self, _unit_type: core::unit::UnitType) -> bool { + true + } } impl ReStation for TimerManager { diff --git a/core/coms/timer/src/rentry.rs b/core/coms/timer/src/rentry.rs index da0e368e..4dce7e24 100644 --- a/core/coms/timer/src/rentry.rs +++ b/core/coms/timer/src/rentry.rs @@ -11,7 +11,10 @@ // See the Mulan PSL v2 for more details. #![allow(non_snake_case)] use basic::time::{parse_timer, USEC_INFINITY}; -use core::rel::{ReDb, ReDbRwTxn, ReDbTable, ReliSwitch, Reliability}; +use core::{ + rel::{ReDb, ReDbRwTxn, ReDbTable, ReliSwitch, Reliability}, + Error, +}; use macros::{EnumDisplay, UnitSection}; use serde::{Deserialize, Serialize}; use std::rc::Rc; @@ -100,6 +103,41 @@ pub struct SectionTimer { pub RemainAfterElapse: bool, } +impl SectionTimer { + pub(super) fn set_property( + &mut self, + key: &str, + value: &str, + ) -> Result<(), core::error::Error> { + match key { + "AccuracySec" => self.AccuracySec = parse_timer(value)?, + "RandomizedDelaySec" => self.RandomizedDelaySec = parse_timer(value)?, + "OnActiveSec" => self.OnActiveSec = parse_timer(value)?, + "OnBootSec" => self.OnBootSec = parse_timer(value)?, + "OnStartupSec" => self.OnStartupSec = parse_timer(value)?, + "OnUnitActiveSec" => self.OnUnitActiveSec = parse_timer(value)?, + "OnUnitInactiveSec" => self.OnUnitInactiveSec = parse_timer(value)?, + "OnCalendar" => self.OnCalendar = parse_timer(value)?, + "Unit" => { + self.Unit = if value.is_empty() { + None + } else { + Some(value.to_string()) + } + } + "WakeSystem" => self.WakeSystem = basic::config::parse_boolean(value)?, + "Persistent" => self.Persistent = basic::config::parse_boolean(value)?, + "RemainAfterElapse" => self.RemainAfterElapse = basic::config::parse_boolean(value)?, + str_key => { + return Err(Error::NotFound { + what: format!("set timer property:{}", str_key), + }); + } + } + Ok(()) + } +} + #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)] pub struct TimerValue { pub timerbase: TimerBase, diff --git a/core/coms/timer/src/unit.rs b/core/coms/timer/src/unit.rs index c2b07210..f8d18176 100644 --- a/core/coms/timer/src/unit.rs +++ b/core/coms/timer/src/unit.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use crate::{ + bus::TimerBus, comm::TimerUnitComm, config::TimerConfig, load::TimerLoad, @@ -29,6 +30,7 @@ struct TimerUnit { config: Rc, mng: Rc, load: TimerLoad, + bus: TimerBus, } impl ReStation for TimerUnit { @@ -110,6 +112,15 @@ impl SubUnit for TimerUnit { fn trigger_notify(&self) { self.mng.trigger_notify() } + + fn unit_set_property( + &self, + key: &str, + value: &str, + flags: core::unit::UnitWriteFlags, + ) -> Result<()> { + self.bus.unit_set_property(key, value, flags) + } } impl UnitMngUtil for TimerUnit { @@ -136,6 +147,7 @@ impl TimerUnit { config: Rc::clone(&config), mng: Rc::clone(&mng), load: TimerLoad::new(&config, &comm), + bus: TimerBus::new(&comm, &config), } } diff --git a/core/libcore/src/unit/base.rs b/core/libcore/src/unit/base.rs index e6d7c909..7ce494d4 100644 --- a/core/libcore/src/unit/base.rs +++ b/core/libcore/src/unit/base.rs @@ -222,11 +222,11 @@ macro_rules! declare_unitobj_plugin_with_param { #[cfg_attr(feature = "plugin", no_mangle)] pub fn __subunit_create_with_params( um: Rc, - ) -> *const dyn $crate::unit::SubUnit { + ) -> *mut dyn $crate::unit::SubUnit { let construcotr: fn(um: Rc) -> $unit_type = $constructor; let obj = construcotr(um); - let rced: Rc = Rc::new(obj); - Rc::into_raw(rced) + let boxed: Box = Box::new(obj); + Box::into_raw(boxed) } }; } diff --git a/core/libcore/src/unit/mod.rs b/core/libcore/src/unit/mod.rs index 0d130501..f2550079 100644 --- a/core/libcore/src/unit/mod.rs +++ b/core/libcore/src/unit/mod.rs @@ -12,9 +12,9 @@ //! pub use base::{unit_name_is_valid, SubUnit, UnitBase, UnitNameFlags}; -pub use deps::{unit_name_to_type, - unit_write_flags_is_noop, UnitDependencyMask, UnitRelationAtom, UnitRelations, UnitType, - UnitWriteFlags, +pub use deps::{ + unit_name_to_type, unit_write_flags_is_noop, UnitDependencyMask, UnitRelationAtom, + UnitRelations, UnitType, UnitWriteFlags, }; pub use kill::{KillContext, KillMode, KillOperation}; pub use path_spec::{PathSpec, PathType}; diff --git a/core/sctl/src/main.rs b/core/sctl/src/main.rs index cea5f249..313ed9ae 100644 --- a/core/sctl/src/main.rs +++ b/core/sctl/src/main.rs @@ -21,7 +21,7 @@ use cmdproto::{ mngr_comm, unit_file, ProstClientStream, }, }; -use constants::SCTL_SOCKET; +use constants::PRIVATE_SOCKET; use std::process::exit; use std::{io::Write, os::unix::net::UnixStream}; @@ -258,7 +258,7 @@ fn main() { Some(v) => v, }; - let stream = match UnixStream::connect(SCTL_SOCKET) { + let stream = match UnixStream::connect(PRIVATE_SOCKET) { Err(e) => { eprintln!("Failed to connect to sysmaster: {}", e); exit(e.raw_os_error().unwrap()); diff --git a/core/sysmaster/src/manager/commands.rs b/core/sysmaster/src/manager/commands.rs index 279eae58..5a82d611 100644 --- a/core/sysmaster/src/manager/commands.rs +++ b/core/sysmaster/src/manager/commands.rs @@ -21,7 +21,7 @@ use std::os::unix::io::RawFd; use std::path::Path; use std::{os::unix::prelude::AsRawFd, rc::Rc}; -use constants::SCTL_SOCKET; +use constants::PRIVATE_SOCKET; pub(super) struct Commands { // associated objects @@ -38,7 +38,7 @@ where { pub(super) fn new(relir: &Rc, comm_action: T) -> Self { /* The socket is used to communicate with sctl, panic if any of the following steps fail. */ - let sctl_socket_path = Path::new(SCTL_SOCKET); + let sctl_socket_path = Path::new(PRIVATE_SOCKET); let run_sysmaster = sctl_socket_path.parent().unwrap(); if run_sysmaster.exists() { let _ = fs::chmod("/run/sysmaster", 0o755); @@ -47,7 +47,7 @@ where if sctl_socket_path.exists() && !is_symlink(sctl_socket_path) { do_entry_log!(std::fs::remove_file, sctl_socket_path, "remove"); } - let sctl_socket_addr = socket::UnixAddr::new(Path::new(SCTL_SOCKET)).unwrap(); + let sctl_socket_addr = socket::UnixAddr::new(Path::new(PRIVATE_SOCKET)).unwrap(); let socket_fd = socket::socket( socket::AddressFamily::Unix, socket::SockType::Stream, @@ -150,7 +150,7 @@ mod tests { use cmdproto::error::Result; use cmdproto::proto::{execute::ExecuterAction, unit_comm}; use cmdproto::proto::{CommandRequest, ProstClientStream}; - use constants::SCTL_SOCKET; + use constants::PRIVATE_SOCKET; use core::rel::{ReliConf, Reliability}; use event::{EventState, Events}; use nix::unistd; @@ -236,6 +236,15 @@ mod tests { fn switch_root(&self, _init: &[String]) -> Result<(), Self::Error> { Ok(()) } + + fn start_transient_unit( + &self, + _job_mode: &str, + _unit_config: &cmdproto::proto::transient_unit_comm::UnitConfig, + _aux_units: &[cmdproto::proto::transient_unit_comm::UnitConfig], + ) -> Result<(), Self::Error> { + Ok(()) + } } #[test] @@ -295,7 +304,7 @@ mod tests { unistd::close(pipe_sync.0).unwrap(); /* 1. Start a service as root. */ - let stream = UnixStream::connect(SCTL_SOCKET).unwrap(); + let stream = UnixStream::connect(PRIVATE_SOCKET).unwrap(); let mut client = ProstClientStream::new(stream); let req = CommandRequest::new_unitcomm( @@ -311,7 +320,7 @@ mod tests { } /* 2. Start a service as an unprivileged user. */ let _ = nix::unistd::setuid(nix::unistd::Uid::from_raw(1000)); - let stream = UnixStream::connect(SCTL_SOCKET).unwrap(); + let stream = UnixStream::connect(PRIVATE_SOCKET).unwrap(); let mut client = ProstClientStream::new(stream); let req = CommandRequest::new_unitcomm( @@ -351,7 +360,7 @@ mod tests { /* Wait until the main thread are ready to process our request. */ let mut test_ok = mutex_child.lock().unwrap(); /* 1. Start a service as root. */ - let stream = UnixStream::connect(SCTL_SOCKET).unwrap(); + let stream = UnixStream::connect(PRIVATE_SOCKET).unwrap(); let mut client = ProstClientStream::new(stream); let req = CommandRequest::new_unitcomm( diff --git a/core/sysmaster/src/manager/mod.rs b/core/sysmaster/src/manager/mod.rs index 4483ba36..73199206 100644 --- a/core/sysmaster/src/manager/mod.rs +++ b/core/sysmaster/src/manager/mod.rs @@ -31,6 +31,7 @@ use basic::{CGROUP_SYSMASTER, MULTI_USER_TARGET}; use cgroup::CgController; use cgroup::{cg_create_and_attach, CgFlags}; use cmdproto::proto::execute::ExecuterAction; +use cmdproto::proto::transient_unit_comm::UnitConfig; use commands::Commands; use core::error::*; use core::rel::{ReliConf, ReliLastFrame, Reliability}; @@ -189,6 +190,16 @@ impl ExecuterAction for CommandActionMgr { fn switch_root(&self, init: &[String]) -> Result<(), Self::Error> { self.um.switch_root(init) } + + fn start_transient_unit( + &self, + job_mode: &str, + unit_config: &UnitConfig, + aux_units: &[UnitConfig], + ) -> Result<(), Self::Error> { + self.um + .start_transient_unit(job_mode, unit_config, aux_units) + } } /// Encapsulate manager and expose api to the outside diff --git a/core/sysmaster/src/plugin/mod.rs b/core/sysmaster/src/plugin/mod.rs index 48b8fc71..1cfbeac7 100644 --- a/core/sysmaster/src/plugin/mod.rs +++ b/core/sysmaster/src/plugin/mod.rs @@ -455,7 +455,7 @@ impl Plugin { &self, unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { type FnType = fn(um: Rc) -> *mut dyn SubUnit; let dy_lib = match self.get_lib(unit_type) { @@ -484,8 +484,8 @@ impl Plugin { }; log::debug!("create unit obj with params"); - let rced_raw = fun(um.clone()); - Ok(unsafe { Rc::from_raw(rced_raw) }) + let boxed_raw = fun(um.clone()); + Ok(unsafe { Box::from_raw(boxed_raw) }) } /// Create a obj for subclasses of unit manager /// each sub unit manager need reference of declare_umobj_plugin diff --git a/core/sysmaster/src/unit/bus.rs b/core/sysmaster/src/unit/bus.rs index 0caa110e..b08f180b 100644 --- a/core/sysmaster/src/unit/bus.rs +++ b/core/sysmaster/src/unit/bus.rs @@ -15,7 +15,8 @@ use super::entry::UnitX; use super::rentry::{self, UnitLoadState}; use super::submanager::UnitSubManagers; use super::uload::UnitLoad; -use basic::fs_util::RUN_TRANSIENT_PATH; +use basic::fs::RUN_TRANSIENT_PATH; +use cmdproto::proto::transient_unit_comm::UnitProperty; use core::error::*; use core::rel::Reliability; use core::unit::{self, UnitType, UnitWriteFlags}; @@ -50,7 +51,7 @@ impl UnitBus { pub(super) fn transient_unit_from_message( &self, - properties: &[(&str, &str)], + properties: &[UnitProperty], name: &str, ) -> Result> { let unit_type = rentry::unit_name_to_type(name); @@ -94,7 +95,7 @@ impl UnitBus { !exist } - fn unit_make_transient(&self, unit: &Rc, properties: &[(&str, &str)]) -> Result<()> { + fn unit_make_transient(&self, unit: &Rc, properties: &[UnitProperty]) -> Result<()> { self.reli.set_last_unit(&unit.id()); let ret = self.unit_make_transient_body(unit, properties); self.reli.clear_last_unit(); @@ -107,7 +108,7 @@ impl UnitBus { fn unit_make_transient_body( &self, unit: &Rc, - properties: &[(&str, &str)], + properties: &[UnitProperty], ) -> Result<()> { let name = unit.id(); let path = get_transient_file_path(&name); @@ -122,11 +123,11 @@ impl UnitBus { fn unit_set_properties( &self, unit: &Rc, - properties: &[(&str, &str)], + properties: &[UnitProperty], flags: UnitWriteFlags, ) -> Result<()> { for property in properties { - self.unit_set_property(unit, property.0, property.1, flags)?; + self.unit_set_property(unit, &property.key, &property.value, flags)?; } Ok(()) @@ -195,8 +196,8 @@ impl UnitBus { | "OnSuccess" | "Before" | "After" | "Conflicts" => { self.unit_write_property(unit, &ps, key, value, flags, false) } - _ => Err(Error::NotFound { - what: "set transient property".to_string(), + str_key => Err(Error::NotFound { + what: format!("set transient property:{}", str_key), }), } } @@ -211,8 +212,8 @@ impl UnitBus { let ps = self.sms.private_section(unit.unit_type()); match key { "Description" => self.unit_write_property(unit, &ps, key, value, flags, true), - _ => Err(Error::NotFound { - what: "set live property".to_string(), + str_key => Err(Error::NotFound { + what: format!("set live property:{}", str_key), }), } } diff --git a/core/sysmaster/src/unit/entry/uentry.rs b/core/sysmaster/src/unit/entry/uentry.rs index 1f16200a..eff2da55 100644 --- a/core/sysmaster/src/unit/entry/uentry.rs +++ b/core/sysmaster/src/unit/entry/uentry.rs @@ -50,12 +50,12 @@ pub struct Unit { base: Rc, config: Rc, - load: Rc, + load: UeLoad, child: UeChild, cgroup: UeCgroup, conditions: Rc, start_limit: StartLimit, - sub: Rc, + sub: Box, merged_into: RefCell>>, in_stop_when_bound_queue: RefCell, timestamp: Rc>, @@ -228,7 +228,7 @@ impl Unit { dmr: &Rc, rentryr: &Rc, filer: &Rc, - subr: &Rc, + sub: Box, ) -> Rc { let _base = Rc::new(UeBase::new(rentryr, String::from(name), unit_type)); let _config = Rc::new(UeConfig::new(&_base)); @@ -237,11 +237,11 @@ impl Unit { dm: Rc::clone(dmr), base: Rc::clone(&_base), config: Rc::clone(&_config), - load: Rc::clone(&_load), + load: UeLoad::new(dmr, filer, &_base, &_config), child: UeChild::new(&_base), cgroup: UeCgroup::new(&_base), conditions: Rc::new(UeCondition::new()), - sub: Rc::clone(subr), + sub, start_limit: StartLimit::new(), merged_into: RefCell::new(None), in_stop_when_bound_queue: RefCell::new(false), @@ -956,7 +956,7 @@ mod tests { &Rc::new(dm), &rentry, &Rc::new(unit_file), - &sub_obj, + sub_obj, ) } diff --git a/core/sysmaster/src/unit/entry/unitx.rs b/core/sysmaster/src/unit/entry/unitx.rs index 67a93389..e88bcae7 100644 --- a/core/sysmaster/src/unit/entry/unitx.rs +++ b/core/sysmaster/src/unit/entry/unitx.rs @@ -63,9 +63,9 @@ impl UnitX { filer: &Rc, unit_type: UnitType, name: &str, - subclass: Rc, + subclass: Box, ) -> UnitX { - let unit = Unit::new(unit_type, name, dmr, rentryr, filer, &subclass); + let unit = Unit::new(unit_type, name, dmr, rentryr, filer, subclass); UnitX(unit) } diff --git a/core/sysmaster/src/unit/manager.rs b/core/sysmaster/src/unit/manager.rs index cc5c74b0..94c97e43 100644 --- a/core/sysmaster/src/unit/manager.rs +++ b/core/sysmaster/src/unit/manager.rs @@ -40,6 +40,7 @@ use basic::fs::LookupPaths; use basic::show_table::{CellColor, ShowTable}; use basic::time::UnitTimeStamp; use basic::{machine, process, rlimit, signal}; +use cmdproto::proto::transient_unit_comm::UnitConfig; use constants::SIG_SWITCH_ROOT_OFFSET; use core::error::*; use core::exec::ExecParameters; @@ -293,6 +294,16 @@ impl UnitManagerX { }) } } + + pub(crate) fn start_transient_unit( + &self, + job_mode: &str, + unit_config: &UnitConfig, + aux_units: &[UnitConfig], + ) -> Result<()> { + self.data + .start_transient_unit(job_mode, unit_config, aux_units) + } } /// the struct for manager the unit instance @@ -504,7 +515,7 @@ impl UmIf for UnitManager { match self.rentry.load_get(&trigger) { Some(state) => { - if state == UnitLoadState::Loaded { + if state.0 == UnitLoadState::Loaded { return true; } log::error!( @@ -981,12 +992,11 @@ impl UnitManager { } } - #[allow(dead_code)] pub(self) fn start_transient_unit( &self, - properties: &[(&str, &str)], - name: &str, job_mode_str: &str, + unit_config: &UnitConfig, + aux_units: &[UnitConfig], ) -> Result<()> { let job_mode = JobMode::from_str(job_mode_str); if let Err(e) = job_mode { @@ -994,15 +1004,25 @@ impl UnitManager { return Err(Error::InvalidData); } - let unit = self.bus.transient_unit_from_message(properties, name); - if let Err(e) = unit { - log::info!("Failed to get transient unit with err: {}", e); - return Err(e); + let unit = self + .bus + .transient_unit_from_message(&unit_config.unit_properties, &unit_config.unit_name) + .map_err(|e| { + log::info!("Failed to get transient unit with err: {}", e); + e + })?; + + for unit_config in aux_units { + self.bus + .transient_unit_from_message(&unit_config.unit_properties, &unit_config.unit_name) + .map_err(|e| { + log::info!("Failed to get transient aux unit with err: {}", e); + e + })?; } - let u = unit.unwrap(); self.jm.exec( - &JobConf::new(&u, JobKind::Start), + &JobConf::new(&unit, JobKind::Start), job_mode.unwrap(), &mut JobAffect::new(false), )?; diff --git a/core/sysmaster/src/unit/rentry.rs b/core/sysmaster/src/unit/rentry.rs index a82d8add..ee888f09 100644 --- a/core/sysmaster/src/unit/rentry.rs +++ b/core/sysmaster/src/unit/rentry.rs @@ -301,9 +301,9 @@ impl UeConfigUnit { "After" => self.After = vec_str_2_string(value), "Conflicts" => self.Conflicts = vec_str_2_string(value), "Description" => self.Description = value.to_string(), - _ => { + str_key => { return Err(Error::NotFound { - what: "set property".to_string(), + what: format!("set property:{}", str_key), }) } }; diff --git a/core/sysmaster/src/unit/util/unit_om.rs b/core/sysmaster/src/unit/util/unit_om.rs index 64842b3f..476448c0 100644 --- a/core/sysmaster/src/unit/util/unit_om.rs +++ b/core/sysmaster/src/unit/util/unit_om.rs @@ -20,7 +20,7 @@ pub(crate) fn create_um_obj( pub(crate) fn create_subunit_with_um( unit_type: UnitType, um: Rc, -) -> Result> { +) -> Result> { #[cfg(feature = "noplugin")] return noplugin::create_subunit_with_um(unit_type, um); #[cfg(feature = "plugin")] @@ -47,7 +47,7 @@ mod plugin { pub(super) fn create_subunit_with_um( unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { Plugin::get_instance().create_subunit_with_um(unit_type, um) } } @@ -106,7 +106,7 @@ mod noplugin { pub(super) fn create_subunit_with_um( unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { let fun = match unit_type { #[cfg(feature = "mount")] UnitType::UnitMount => mount::__subunit_create_with_params, @@ -127,6 +127,6 @@ mod noplugin { } }; let boxed_raw = fun(um.clone()); - Ok(unsafe { Rc::from_raw(boxed_raw) }) + Ok(unsafe { Box::from_raw(boxed_raw) }) } } diff --git a/exts/run/Cargo.toml b/exts/run/Cargo.toml new file mode 100644 index 00000000..fefef932 --- /dev/null +++ b/exts/run/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "sysmaster-run" +version = "0.5.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nix = { version = "0.24", default-features = false } +log = { path = "../../libs/log", default-features = false } +clap = { version = "3.1.8", features = [ + "derive", + "std", +], default-features = false } +constants = { path = "../../libs/constants" } +cmdproto = { path = "../../libs/cmdproto" } +basic = { path = "../../libs/basic", default-features = false, features = [ + "env", + "id128", +] } +core = { path = "../../core/libcore", default-features = false } diff --git a/exts/run/src/main.rs b/exts/run/src/main.rs new file mode 100644 index 00000000..29fed199 --- /dev/null +++ b/exts/run/src/main.rs @@ -0,0 +1,690 @@ +// 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. + +//! sysmaster-run + +#![allow(deprecated)] +use basic::env; +use basic::id128; +use clap::Parser; +use cmdproto::proto::transient_unit_comm::UnitConfig; +use cmdproto::proto::transient_unit_comm::UnitProperty; +use cmdproto::proto::ProstClientStream; +use cmdproto::{error::ERROR_CODE_MASK_PRINT_STDOUT, proto::abi::CommandRequest}; +use constants::PRIVATE_SOCKET; +use core::unit; +use core::unit::unit_name_is_valid; +use core::unit::UnitNameFlags; +use log::Level; +use std::path::PathBuf; +use std::process::exit; +use std::str::FromStr; +use std::{io::Write, os::unix::net::UnixStream}; + +/// parse program arguments +#[derive(Parser, Debug, Default)] +#[clap(author, version, about, long_about = None)] + +struct Args { + /// unit name + #[clap(long, default_value_t = String::new())] + unit: String, + + /// property + #[clap(short('p'), long)] + property: Vec, + + /// path property + #[clap(long)] + path_property: Vec, + + /// socket property + #[clap(long)] + socket_property: Vec, + + /// timer property + #[clap(long)] + timer_property: Vec, + + /// description + #[clap(long, default_value_t = String::new())] + description: String, + + /// remain-after-exit + #[clap(long, short('r'), required = false)] + remain_after_exit: bool, + + /// service type + #[clap(long, default_value_t = String::new())] + service_type: String, + + /// uid + #[clap(long, default_value_t = String::new())] + uid: String, + + /// gid + #[clap(long, default_value_t = String::new())] + gid: String, + + /// working directory + #[clap(long, default_value_t = String::new())] + working_directory: String, + + /// same dir + #[clap(short('d'), long, required = false)] + same_dir: bool, + + /// set env + #[clap(short('E'), long)] + setenv: Vec, + + /// no ask password + #[clap(long, required = false)] + no_ask_password: bool, + + /// no block + #[clap(long, required = false)] + no_block: bool, + + /// user + #[clap(long, required = false)] + user: bool, + + /// system + #[clap(long, required = false)] + system: bool, + + /// send sighup + #[clap(long, required = false)] + send_sighup: bool, + + /// pty + #[clap(short('t'), long, required = false)] + pty: bool, + + /// pipe + #[clap(short('P'), long, required = false)] + pipe: bool, + + /// quiet + #[clap(short('q'), long, required = false)] + quiet: bool, + + /// on active + #[clap(long, default_value_t = String::new())] + on_active: String, + + /// on boot + #[clap(long, default_value_t = String::new())] + on_boot: String, + + /// on startup + #[clap(long, default_value_t = String::new())] + on_startup: String, + + /// on unit active + #[clap(long, default_value_t = String::new())] + on_unit_active: String, + + /// on unit inactive + #[clap(long, default_value_t = String::new())] + on_unit_inactive: String, + + /// on calendar + #[clap(long, default_value_t = String::new())] + on_calendar: String, + + /// collect + #[clap(short('G'), long, required = false)] + collect: bool, + + /// args cmdline + #[clap()] + args_cmdline: Vec, +} + +fn deal_working_directory(working_directory: &str) -> basic::error::Result { + let path_buf = if working_directory.starts_with('/') { + PathBuf::from(working_directory) + } else { + match std::env::current_dir() { + Ok(mut dir) => { + dir.push(working_directory); + dir + } + Err(e) => { + log::error!("Failed to get current working directory: {}", e); + return Err(basic::Error::Io { source: e }); + } + } + }; + + match path_buf.canonicalize() { + Ok(path_buf) => Ok(path_buf.to_string_lossy().to_string()), + Err(e) => { + log::error!( + "Failed to parse path {} and make it absolute: {}", + working_directory, + e + ); + Err(basic::Error::Io { source: e }) + } + } +} + +fn parse_args(args: &mut Args) -> basic::error::Result<()> { + if args.user { + args.no_ask_password = true; + } + + if !args.working_directory.is_empty() { + args.working_directory = deal_working_directory(&args.working_directory)?; + } + + if args.same_dir { + args.working_directory = match std::env::current_dir() { + Ok(dir) => dir.to_string_lossy().to_string(), + Err(e) => { + log::error!("Failed to get current working directory: {}", e); + return Err(basic::Error::Io { source: e }); + } + }; + } + + for env in &args.setenv { + env::Env::new(env)?; + } + + let mut arg_with_timer = false; + for timer_property in &args.timer_property { + for start in [ + "OnActiveSec=", + "OnBootSec=", + "OnStartupSec=", + "OnUnitActiveSec=", + "OnUnitInactiveSec=", + "OnCalendar=", + ] { + if timer_property.starts_with(start) { + arg_with_timer = true; + break; + } + } + } + + arg_with_timer = arg_with_timer + || !args.on_active.is_empty() + || !args.on_boot.is_empty() + || !args.on_calendar.is_empty() + || !args.on_startup.is_empty() + || !args.on_unit_inactive.is_empty() + || !args.on_unit_active.is_empty(); + + if !args.on_active.is_empty() { + args.timer_property + .push(format!("OnActiveSec={}", args.on_active)); + } + if !args.on_boot.is_empty() { + args.timer_property + .push(format!("OnBootSec={}", args.on_boot)); + } + if !args.on_calendar.is_empty() { + args.timer_property + .push(format!("OnCalendar={}", args.on_calendar)); + } + if !args.on_startup.is_empty() { + args.timer_property + .push(format!("OnStartupSec={}", args.on_startup)); + } + if !args.on_unit_inactive.is_empty() { + args.timer_property + .push(format!("OnUnitInactiveSec={}", args.on_unit_inactive)); + } + if !args.on_unit_active.is_empty() { + args.timer_property + .push(format!("OnUnitActiveSec={}", args.on_unit_active)); + } + + let with_trigger = + !args.path_property.is_empty() || !args.socket_property.is_empty() || arg_with_timer; + + if (args.path_property.len() + args.socket_property.len() + if arg_with_timer { 1 } else { 0 }) + > 1 + { + log::error!("Only single trigger (path, socket, timer) unit can be created."); + return Err(basic::Error::Other { + msg: "Only single trigger (path, socket, timer) unit can be created.".to_string(), + }); + } + + if args.args_cmdline.is_empty() && args.unit.is_empty() && !with_trigger { + log::error!("Command line to execute required."); + return Err(basic::Error::Other { + msg: "Command line to execute required.".to_string(), + }); + } + + if !args.timer_property.is_empty() && !arg_with_timer { + log::error!("--timer-property= has no effect without any other timer options."); + return Err(basic::Error::Other { + msg: "--timer-property= has no effect without any other timer options.".to_string(), + }); + } + + if args.description.is_empty() { + args.description = args + .args_cmdline + .iter() + .fold(String::new(), |args, arg| args + arg + " ") + .trim() + .to_string(); + } + + Ok(()) +} + +fn get_unit_name(name: &str, suffix: &str) -> nix::Result { + let unit_name = if name.is_empty() { + format!( + "run-r{}{}", + basic::id128::id128_randomize(id128::Id128FormatFlag::ID128_FORMAT_PLAIN).map_err( + |e| { + log::error!("Failed to generate random run unit name:{}", e); + e + } + )?, + suffix + ) + } else if name.ends_with(suffix) { + name.to_string() + } else { + format!("{}{}", name, suffix) + }; + + if !unit_name_is_valid(&unit_name, UnitNameFlags::PLAIN) { + log::debug!("Invalid unit name: {}", name); + return Err(nix::errno::Errno::EINVAL); + } + + Ok(unit_name) +} + +fn unit_suffix_is_valid(suffix: &str) -> bool { + if suffix.is_empty() { + return false; + } + if !suffix.starts_with('.') { + return false; + } + + unit::UnitType::from_str(&suffix[1..]).map_or(false, |unit_type| { + unit_type != unit::UnitType::UnitTypeInvalid + }) +} + +fn unit_name_change_suffix(name: &str, suffix: &str) -> nix::Result { + if !unit_name_is_valid(name, UnitNameFlags::PLAIN) { + return Err(nix::errno::Errno::EINVAL); + } + + if !unit_suffix_is_valid(suffix) { + return Err(nix::errno::Errno::EINVAL); + } + + let unit_name = match name.rfind('.') { + Some(pos) => format!("{}{}", name[..pos].to_string(), suffix.to_string()), + None => return Err(nix::errno::Errno::EINVAL), + }; + + if !unit_name_is_valid(&unit_name, UnitNameFlags::PLAIN) { + return Err(nix::errno::Errno::EINVAL); + } + + Ok(unit_name) +} + +fn to_unit_property(property: &str) -> nix::Result { + if property.is_empty() { + return Err(nix::errno::Errno::EINVAL); + } + + match property.find('=') { + None => Err(nix::errno::Errno::EINVAL), + Some(pos) => { + if pos == 0 { + Err(nix::errno::Errno::EINVAL) + } else { + Ok(UnitProperty { + key: property[0..pos].to_string(), + value: property[pos + 1..].to_string(), + }) + } + } + } +} + +fn transient_unit_set_properties( + description: &str, + collect: bool, + properties: &[String], +) -> nix::Result> { + let mut unit_properties = vec![]; + if !description.is_empty() { + unit_properties.push(UnitProperty { + key: "Description".to_string(), + value: description.to_string(), + }); + } + + if collect { + unit_properties.push(UnitProperty { + key: "CollectMode".to_string(), + value: "inactive-or-failed".to_string(), + }); + } + + for property in properties { + unit_properties.push(to_unit_property(property)?); + } + Ok(unit_properties) +} + +fn transient_service_set_properties(args: &Args) -> nix::Result> { + let mut properties = + transient_unit_set_properties(&args.description, args.collect, &args.property)?; + + if args.send_sighup { + properties.push(UnitProperty { + key: "SendSIGHUP".to_string(), + value: "true".to_string(), + }); + } + + if args.remain_after_exit { + properties.push(UnitProperty { + key: "RemainAfterExit".to_string(), + value: "true".to_string(), + }); + } + + if !args.service_type.is_empty() { + properties.push(UnitProperty { + key: "Type".to_string(), + value: args.service_type.clone(), + }); + } + + if !args.uid.is_empty() { + properties.push(UnitProperty { + key: "User".to_string(), + value: args.uid.clone(), + }); + } + + if !args.gid.is_empty() { + properties.push(UnitProperty { + key: "Group".to_string(), + value: args.gid.clone(), + }); + } + + if !args.working_directory.is_empty() { + properties.push(UnitProperty { + key: "WorkingDirectory".to_string(), + value: args.working_directory.clone(), + }); + } + + if !args.setenv.is_empty() { + let mut envs = String::new(); + for env in &args.setenv { + if !env::Env::is_valid(env) { + return Err(nix::errno::Errno::EINVAL); + } + envs.push_str(env); + envs.push(' '); + } + properties.push(UnitProperty { + key: "Environment".to_string(), + value: envs.trim().to_string(), + }); + } + + if !args.args_cmdline.is_empty() { + properties.push(UnitProperty { + key: "ExecStart".to_string(), + value: args + .args_cmdline + .iter() + .fold(String::new(), |args, arg| args + arg + " "), + }); + } + Ok(properties) +} + +fn generate_command_request(args: Args) -> nix::Result { + let unit_name; + let mut unit_properties; + let aux_name; + let aux_unit_properties; + + if !args.path_property.is_empty() { + unit_name = get_unit_name(&args.unit, ".path")?; + unit_properties = + transient_unit_set_properties(&args.description, args.collect, &args.path_property)?; + aux_name = unit_name_change_suffix(&unit_name, ".service")?; + aux_unit_properties = transient_service_set_properties(&args)?; + } else if !args.socket_property.is_empty() { + unit_name = get_unit_name(&args.unit, ".socket")?; + unit_properties = + transient_unit_set_properties(&args.description, args.collect, &args.socket_property)?; + aux_name = unit_name_change_suffix(&unit_name, ".service")?; + aux_unit_properties = transient_service_set_properties(&args)?; + } else if !args.timer_property.is_empty() { + unit_name = get_unit_name(&args.unit, ".timer")?; + aux_name = unit_name_change_suffix(&unit_name, ".service")?; + unit_properties = + transient_unit_set_properties(&args.description, args.collect, &args.timer_property)?; + aux_unit_properties = transient_service_set_properties(&args)?; + unit_properties.push(UnitProperty { + key: "RemainAfterElapse".to_string(), + value: "false".to_string(), + }); + } else { + unit_name = get_unit_name(&args.unit, ".service")?; + unit_properties = transient_service_set_properties(&args)?; + aux_name = "".to_string(); + aux_unit_properties = vec![]; + } + + let mut aux_units: Vec = vec![]; + if !aux_name.is_empty() { + aux_units.push(UnitConfig { + unit_name: aux_name, + unit_properties: aux_unit_properties, + }) + } + + let s = CommandRequest::new_transient_unit_comm( + "fail", + &UnitConfig { + unit_name, + unit_properties, + }, + &aux_units, + ); + Ok(s) +} + +fn main() { + log::init_log_to_console("sysmaster-run", Level::Debug); + let mut args = Args::parse(); + if let Err(e) = parse_args(&mut args) { + log::debug!("parse args error: {}", e); + std::process::exit(-1); + } + + let command_request = match generate_command_request(args) { + Err(e) => { + eprintln!("Unknown unit name or property:{}", e); + exit(e as i32); + } + Ok(v) => v, + }; + + let stream = match UnixStream::connect(PRIVATE_SOCKET) { + Err(e) => { + eprintln!("Failed to connect to sysmaster: {}", e); + exit(e.raw_os_error().unwrap()); + } + Ok(v) => v, + }; + + let mut client = ProstClientStream::new(stream); + + let data = match client.execute(command_request) { + Err(e) => { + eprintln!("Failed to execute the given command: {}", e); + exit(1); + } + Ok(v) => v, + }; + + /* We should always print the error message if the returned error code is not 0. */ + if data.message.is_empty() { + exit(0); + } + + if data.error_code == 0 || (data.error_code & ERROR_CODE_MASK_PRINT_STDOUT != 0) { + /* Don't care if we fail to write the message out. */ + let _ = writeln!(std::io::stdout(), "{}", data.message); + } else { + eprintln!("{}", data.message); + } + + exit((data.error_code & !ERROR_CODE_MASK_PRINT_STDOUT) as i32); +} +#[cfg(test)] +mod tests { + use super::*; + use std::fs::{create_dir, remove_dir_all}; + + #[test] + fn test_get_unit_name() { + assert_eq!( + get_unit_name("test", ".service").unwrap(), + "test.service".to_string() + ); + assert_eq!( + get_unit_name("test.service", ".service").unwrap(), + "test.service".to_string() + ); + assert!(get_unit_name("", ".service").unwrap().starts_with("run-r")); + assert!(get_unit_name("test.mount", ".service").is_err()); + } + + #[test] + fn test_unit_suffix_is_valid() { + assert!(unit_suffix_is_valid(".service")); + assert!(unit_suffix_is_valid(".socket")); + assert!(!unit_suffix_is_valid("service")); + assert!(!unit_suffix_is_valid(".test")); + } + + #[test] + fn test_deal_working_directory() { + let abs_path = "/root".to_string(); + assert_eq!(deal_working_directory(&abs_path).unwrap(), abs_path); + let rel_path = "test"; + let mut cur_path = std::env::current_dir().unwrap(); + cur_path.push(rel_path); + create_dir(rel_path).unwrap(); + assert_eq!( + deal_working_directory(rel_path).unwrap(), + cur_path.to_string_lossy().to_string() + ); + remove_dir_all(rel_path).unwrap(); + } + + #[test] + fn test_parse_args() { + let mut args = Args::parse_from(vec![ + "sysmaster-run", + "--unit", + "test", + "--user", + "--same-dir", + "--remain-after-exit", + "-E", + "aa=bb", + "/bin/sleep", + ]); + assert!(parse_args(&mut args).is_ok()); + assert!(args.user); + assert!(args.no_ask_password); + assert_eq!( + args.working_directory, + std::env::current_dir() + .unwrap() + .to_string_lossy() + .to_string() + ); + let mut args = Args::parse_from(vec![ + "sysmaster-run", + "--unit", + "test", + "--socket-property", + "ListenStream=/tmp/server.socket", + "sleep", + ]); + assert!(parse_args(&mut args).is_ok()); + assert!(parse_args(&mut Args::parse_from(vec![ + "sysmaster-run", + "--unit", + "test", + "--socket-property", + "ListenStream=/tmp/server.socket", + "--timer-property", + "OnActiveSec=1", + "--on-boot", + "1", + "sleep" + ])) + .is_err()); + } + + #[test] + fn test_to_unit_property() { + assert_eq!( + to_unit_property("aa=bb").unwrap(), + UnitProperty { + key: "aa".to_string(), + value: "bb".to_string() + } + ); + assert_eq!( + to_unit_property("aa==bb").unwrap(), + UnitProperty { + key: "aa".to_string(), + value: "=bb".to_string() + } + ); + assert_eq!( + to_unit_property("aa=").unwrap(), + UnitProperty { + key: "aa".to_string(), + value: "".to_string() + } + ); + assert!(to_unit_property("=").is_err()); + } +} diff --git a/libs/basic/Cargo.toml b/libs/basic/Cargo.toml index d62f86b5..c8f3be49 100644 --- a/libs/basic/Cargo.toml +++ b/libs/basic/Cargo.toml @@ -71,6 +71,7 @@ full = [ "time", "path", "glob", + "env", ] capability = [] @@ -125,3 +126,4 @@ uuid = ["bitflags", "random"] time = [] path = [] glob = [] +env = [] diff --git a/libs/basic/src/env.rs b/libs/basic/src/env.rs new file mode 100644 index 00000000..a8ca7143 --- /dev/null +++ b/libs/basic/src/env.rs @@ -0,0 +1,102 @@ +// 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::error::*; +use nix::unistd; +/// struct Env +pub struct Env(String); + +impl Env { + ///create a new instance of Env + pub fn new(input: &str) -> Result { + Env::from_str(input) + } + + fn from_str(input: &str) -> Result { + if Env::is_valid(input) { + Ok(Env(input.to_string())) + } else { + Err(Error::Invalid { + what: " Invalid Env".to_string(), + }) + } + } + + ///Check whether environment variables are valid + pub fn is_valid(input: &str) -> bool { + let pos = match input.find('=') { + Some(pos) => pos, + None => return false, + }; + Env::is_valid_name(&input[0..pos]) && Env::is_valid_value(&input[pos + 1..]) + } + + fn is_valid_name(str: &str) -> bool { + if str.is_empty() { + return false; + } + + let str_bytes: &[u8] = str.as_bytes(); + + if str_bytes[0].is_ascii_digit() { + return false; + } + + let arg_max = + unistd::sysconf(unistd::SysconfVar::ARG_MAX).map_or(2, |f| f.map_or(2, |s| s)) - 2; + if str_bytes.len() > arg_max as usize { + return false; + } + + for s in str_bytes { + if !(s.is_ascii_alphanumeric() || *s == b'_') { + return false; + } + } + true + } + + fn is_valid_value(str: &str) -> bool { + let arg_max = + unistd::sysconf(unistd::SysconfVar::ARG_MAX).map_or(3, |f| f.map_or(3, |s| s)) - 3; + if str.as_bytes().len() > arg_max as usize { + return false; + } + true + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_env() { + assert!(Env::new("a=").is_ok()); + assert!(Env::new("b=głąb kapuściany").is_ok()); + assert!(Env::new("c=\\007\\009\\011").is_ok()); + assert!(Env::new("e=printf \"\\x1b]0;\\x07\"").is_ok()); + assert!(Env::new("f=tab\tcharacter").is_ok()); + assert!(Env::new("g=new\nline").is_ok()); + + assert!(Env::new("=").is_err()); + assert!(Env::new("a b=").is_err()); + assert!(Env::new("a =").is_err()); + assert!(Env::new(" b=").is_err()); + assert!(Env::new("a.b=").is_err()); + assert!(Env::new("a-b=").is_err()); + assert!(Env::new("\007=głąb kapuściany").is_err()); + assert!(Env::new("c\009=\007\009\011").is_err()); + assert!(Env::new("głąb=printf \"\x1b]0;\x07\"").is_err()); + } +} diff --git a/libs/basic/src/lib.rs b/libs/basic/src/lib.rs index e3655f22..de003950 100644 --- a/libs/basic/src/lib.rs +++ b/libs/basic/src/lib.rs @@ -27,6 +27,8 @@ pub mod config; pub mod cpu; #[cfg(feature = "disk")] pub mod disk; +#[cfg(feature = "env")] +pub mod env; #[cfg(feature = "exec")] pub mod exec; #[cfg(feature = "fd")] diff --git a/libs/cmdproto/src/proto/abi.proto b/libs/cmdproto/src/proto/abi.proto index 536042e9..109cf563 100644 --- a/libs/cmdproto/src/proto/abi.proto +++ b/libs/cmdproto/src/proto/abi.proto @@ -17,6 +17,8 @@ message CommandRequest { SysComm syscomm = 5; //switch root commands SwitchRootComm srcomm = 6; + //transient unit commands + TransientUnitComm trancomm = 7; } } @@ -93,3 +95,19 @@ message SysComm { message SwitchRootComm { repeated string init = 1; } + + +message TransientUnitComm { + message UnitProperty { + string key = 1; + string value = 2; + } + + message UnitConfig { + string unitName = 1; + repeated UnitProperty unit_properties = 2; + } + string job_mode = 1; + UnitConfig unit_config = 2; + repeated UnitConfig aux_units = 3; +} diff --git a/libs/cmdproto/src/proto/abi.rs b/libs/cmdproto/src/proto/abi.rs index edd9c59d..c3bff6d5 100644 --- a/libs/cmdproto/src/proto/abi.rs +++ b/libs/cmdproto/src/proto/abi.rs @@ -2,7 +2,7 @@ #[rustfmt::skip] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CommandRequest { - #[prost(oneof="command_request::RequestData", tags="1, 2, 3, 4, 5, 6")] + #[prost(oneof="command_request::RequestData", tags="1, 2, 3, 4, 5, 6, 7")] pub request_data: ::core::option::Option, } /// Nested message and enum types in `CommandRequest`. @@ -28,6 +28,9 @@ pub mod command_request { ///switch root commands #[prost(message, tag="6")] Srcomm(super::SwitchRootComm), + ///transient unit commands + #[prost(message, tag="7")] + Trancomm(super::TransientUnitComm), } } /// Command Response from server @@ -154,3 +157,32 @@ pub struct SwitchRootComm { #[prost(string, repeated, tag="1")] pub init: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } +#[rustfmt::skip] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransientUnitComm { + #[prost(string, tag="1")] + pub job_mode: ::prost::alloc::string::String, + #[prost(message, optional, tag="2")] + pub unit_config: ::core::option::Option, + #[prost(message, repeated, tag="3")] + pub aux_units: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `TransientUnitComm`. +pub mod transient_unit_comm { + #[rustfmt::skip] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct UnitProperty { + #[prost(string, tag="1")] + pub key: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub value: ::prost::alloc::string::String, + } + #[rustfmt::skip] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct UnitConfig { + #[prost(string, tag="1")] + pub unit_name: ::prost::alloc::string::String, + #[prost(message, repeated, tag="2")] + pub unit_properties: ::prost::alloc::vec::Vec, + } +} diff --git a/libs/cmdproto/src/proto/execute.rs b/libs/cmdproto/src/proto/execute.rs index c806aab5..03aebca2 100644 --- a/libs/cmdproto/src/proto/execute.rs +++ b/libs/cmdproto/src/proto/execute.rs @@ -12,8 +12,8 @@ //! Convert the command request into the corresponding execution action use super::{ - mngr_comm, sys_comm, unit_comm, CommandRequest, CommandResponse, MngrComm, RequestData, - SwitchRootComm, SysComm, UnitComm, UnitFile, + mngr_comm, sys_comm, transient_unit_comm, unit_comm, CommandRequest, CommandResponse, MngrComm, + RequestData, SwitchRootComm, SysComm, TransientUnitComm, UnitComm, UnitFile, }; use crate::error::*; @@ -75,6 +75,13 @@ pub trait ExecuterAction { fn daemon_reexec(&self); /// switch root fn switch_root(&self, init: &[String]) -> Result<(), Self::Error>; + /// transient unit + fn start_transient_unit( + &self, + job_mode: &str, + unit_config: &transient_unit_comm::UnitConfig, + aux_units: &[transient_unit_comm::UnitConfig], + ) -> Result<(), Self::Error>; } /// Depending on the type of request @@ -103,6 +110,7 @@ where Some(RequestData::Syscomm(param)) => param.execute(manager, Some(call_back), cred), Some(RequestData::Ufile(param)) => param.execute(manager, Some(call_back), cred), Some(RequestData::Srcomm(param)) => param.execute(manager, None, cred), + Some(RequestData::Trancomm(param)) => param.execute(manager, None, cred), _ => CommandResponse::default(), } } @@ -435,3 +443,41 @@ impl Executer for SwitchRootComm { } } } + +impl Executer for TransientUnitComm { + fn execute( + self, + manager: Rc, + _call_back: Option String>, + cred: Option, + ) -> CommandResponse { + if let Some(v) = response_if_credential_dissatisfied(cred, false) { + return v; + } + + if self.unit_config.is_none() { + return CommandResponse { + status: StatusCode::INTERNAL_SERVER_ERROR.as_u16() as _, + error_code: 1, + message: String::from("error."), + }; + } + + match manager.start_transient_unit( + &self.job_mode, + &self.unit_config.unwrap(), + &self.aux_units, + ) { + Ok(_) => CommandResponse { + status: StatusCode::OK.as_u16() as _, + error_code: 0, + ..Default::default() + }, + Err(e) => CommandResponse { + status: StatusCode::INTERNAL_SERVER_ERROR.as_u16() as _, + message: e.to_string(), + error_code: e.into() as u32, + }, + } + } +} diff --git a/libs/cmdproto/src/proto/mod.rs b/libs/cmdproto/src/proto/mod.rs index 47709b3d..d2876ee9 100644 --- a/libs/cmdproto/src/proto/mod.rs +++ b/libs/cmdproto/src/proto/mod.rs @@ -70,6 +70,21 @@ impl CommandRequest { request_data: Some(RequestData::Srcomm(SwitchRootComm { init })), } } + + /// Create a new command request for start transient unit + pub fn new_transient_unit_comm( + job_mode: &str, + unit_config: &transient_unit_comm::UnitConfig, + aux_units: &[transient_unit_comm::UnitConfig], + ) -> Self { + Self { + request_data: Some(RequestData::Trancomm(TransientUnitComm { + job_mode: job_mode.to_string(), + unit_config: Some(unit_config.clone()), + aux_units: aux_units.to_vec(), + })), + } + } } impl fmt::Display for sys_comm::Action { diff --git a/libs/constants/src/lib.rs b/libs/constants/src/lib.rs index 46a3a50a..a23c2901 100644 --- a/libs/constants/src/lib.rs +++ b/libs/constants/src/lib.rs @@ -25,7 +25,7 @@ pub const INIT_SOCKET: &str = "/run/sysmaster/init"; pub const ALIVE: &str = "ALIVE01234567890"; /// Socket used to transfer message between sysmaster and sctl -pub const SCTL_SOCKET: &str = "/run/sysmaster/sctl"; +pub const PRIVATE_SOCKET: &str = "/run/sysmaster/private"; /// Default log file path when LogTarget is configured to "file" pub const LOG_FILE_PATH: &str = "/var/log/sysmaster/sysmaster.log"; -- Gitee