diff --git a/Cargo.lock b/Cargo.lock index 47074ee44dfc88c900ff0816c2b1819986e4cd70..930b42dd3a51ab399866023edff0a8fdf64b32b0 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 03a4e3e19d8b637ed3df814b1b259726a7828aab..878591e2e6e78506e09c0a315f2e88089104294c 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 2c4c4a08cd75d5d030b65335e6496971387a2523..767cbf1b0659458680159aafa5dfdd666f2eedfb 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/mount/src/manager.rs b/core/coms/mount/src/manager.rs index ab8f3039021ab5c6b3c57e31f7808a6478668d0c..d1a3ef40bfe74e01ab96d41806e0c555be1021af 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/path/Cargo.toml b/core/coms/path/Cargo.toml index 9a2573456876a7a5a96270cab4bce2ae55c9e034..45b4189a9d199f9a11de9afaa67122034fcef717 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 0000000000000000000000000000000000000000..12d0fbf37af51bd60011ad5685149750c4bef510 --- /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 70f6eb856429c583a2d9d8e8bb446a1316ea977f..1473db60bde892be9cf417f2847342592258fab9 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 6e6f1f2a1992eb04cea9077603ebd4900a83f07a..c26baf8e44f7f4847a4574a1d5878b1c3c82dff8 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 92bdeefa0c724193dfba3df2625d6b70328e1ff4..568f0a086ce54ccadef80667730a9ec5fc1531a2 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 04d8b4d1cc03269f1ef364b03845830a5ffb276c..794ae1307b8eda24095776196bd3cefe5610bb96 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 c466e34507beba721d469800086356580bedd9c9..cecbfa707ef8703a68ed320367facfa75ae05e2d 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 bc5957d8f02eebe21a032489cbf04e0fd5446cad..c6e596ca5721f89f53923f2d0ae5626db2457fd3 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 new file mode 100755 index 0000000000000000000000000000000000000000..c96997b203addf6ee4df7fad760fcc40222777e1 --- /dev/null +++ b/core/coms/service/src/bus.rs @@ -0,0 +1,165 @@ +// 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) + } + 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<()> { + 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, + ) -> 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/service/src/config.rs b/core/coms/service/src/config.rs index ca776e5e4745704fda21b867c25bc7967a363ad5..440f0ddae2351367d34d9478c3ddd14e2f6dd199 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 87809ff7e0f8e766a743a797863685461ad2692e..df2e35e16b757eea39d7f1609080a6f405b59360 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 420b7c4ebf866c5ce6ecb0bf97e703abd9388d79..12d137a4d817ad75de97c76ff6c3399fd39d9aab 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 bc1acccc6ffa6feb58384a32e653426826bf9824..8022e20dbd515d8f4d5dd9894961b29fd547cc7c 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}; @@ -334,6 +335,66 @@ 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)?, + "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)?, + "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)?, + //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: format!("set property:{}", str_key), + }) + } + }; + Ok(()) + } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/core/coms/service/src/unit.rs b/core/coms/service/src/unit.rs index 4c7db89c26bc24528e5077f0f599433ec833a56a..47dd7169a8f200fdddee6a23da3bb79a5e78caa8 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/Cargo.toml b/core/coms/socket/Cargo.toml index 6ac97a6780ddb767648d38372fdc3d66914a4482..b826dba94b46f633bbd6c72ddcfd0538dc8d6564 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 0000000000000000000000000000000000000000..ac98c3961eb929d1f1426e59f003ca5e8da333a2 --- /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 011b5a5631177b1b68767a4363560db0ab00888f..99a72ae370738ec201a313ec309fe688f7bd96a4 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 8b4e2ccebd82e95f70378ce7e3e0a194684d4399..aa2b31f04746bd67594d1c59ec7094dbe42501b7 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/manager.rs b/core/coms/socket/src/manager.rs index 7f9c484650fc33f3a65855568e2f8e3261e3d96d..51bbb14cabab83157f695a0d2ea09b71809890d5 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/coms/socket/src/rentry.rs b/core/coms/socket/src/rentry.rs index 98575aed59094f8b17b30fbc75234521e26d506f..b5912246e13a35ad7015cf40f64566b6a5900d3d 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 f4834f84171026d0966267492462fee50271940d..7ed75dc3d0fc534d1acc7aed02b65f1aad9af8d4 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 3244dff33e6d892d7c6e9c55451e17e91eb21944..b5f8b6936fb349f1c02fab9c4fdc483a7aebc84e 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 0000000000000000000000000000000000000000..6528d09392b0578b174b23c3f99e1d8d388daef7 --- /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 ad0b42dd4495c9573fc6a287197218ea26fbcce9..c6e7e205372d2c32f5081c9bbbd0ba8d92d45667 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 f4fb969a07e9055dad7ac9c50c341eb7f60f997b..9c833ebd99dd46d270762b5a3ad8f51d61c2fc0d 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 6d87c346eec213858ef30bb807f37857b66a1ed3..ef116f84b315c633f8d3bb1a5c7610e1635dbbf0 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 da0e368ea70ba79e0c089ce2d911ac520db834ed..4dce7e24cb510d306f7058e1458c6bd94d24faec 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 c2b072101ae592cb6c46b79f469673f4a41b1b4f..f8d18176807c5caeedadc05868bba9eea053a646 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/error.rs b/core/libcore/src/error.rs index 28a47d32891e8d49a3ad045b8a9d90833b61f347..d5d78298a772e605df11c55f0cf5ad4f238f6baa 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 b62e29a5efe516f01fedddbe7ad04bf3b06e8009..7ce494d429b9950bb428e10c5fd0070b0f20a126 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) {} diff --git a/core/libcore/src/unit/deps.rs b/core/libcore/src/unit/deps.rs index 876dfef165cfbfb16eb88acc0f562b142f685834..bed8fa6975ec3d828d3d61bf5b32095dd193951c 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 a043be4bb02fc515d9f9cd1286305c6140e372c3..f25500791fb6b96df476b84da67856ca1c25a5bc 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 1468bd9f6467cf1730af90106974db0999c845b7..9af9bc1412d77182b6731616d5233575f1282f12 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 100755 index 0000000000000000000000000000000000000000..26e5e33f1fe3b89fac09e5aa0fe718cb2ef3f39a --- /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/sctl/src/main.rs b/core/sctl/src/main.rs index cea5f2490557a76465f43b6763ff94e55d87659b..313ed9ae0a40318bb1c6099d434faf4a252108b0 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/job/manager.rs b/core/sysmaster/src/job/manager.rs index bcf2d9db96d8a8e046477e86485590cf411e5c5d..1d627d579896f3801660f722b77c59b24111867f 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/manager/commands.rs b/core/sysmaster/src/manager/commands.rs index 279eae58fe366f30a7ed23bb86f50ef1999e8c80..5a82d611356b8ef7bfc1ea16a4a4b2419665a3fa 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 4483ba3617a69a0d0258d222be0d649d8c64515c..731992061db94e533f472167671342bf1bcb23a8 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 d2b621829bb44edbaec14c1a074e51bf90bfdd4c..1cfbeac7f67ee8c0c8ae3e79a8ff48414d8dd900 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))] @@ -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 100755 index 0000000000000000000000000000000000000000..b08f180bd8c5b7f1c170e8a17645a872371f302b --- /dev/null +++ b/core/sysmaster/src/unit/bus.rs @@ -0,0 +1,285 @@ +// 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::RUN_TRANSIENT_PATH; +use cmdproto::proto::transient_unit_comm::UnitProperty; +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: &[UnitProperty], + 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: &[UnitProperty]) -> 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: &[UnitProperty], + ) -> 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: &[UnitProperty], + flags: UnitWriteFlags, + ) -> Result<()> { + for property in properties { + self.unit_set_property(unit, &property.key, &property.value, 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) + } + str_key => Err(Error::NotFound { + what: format!("set transient property:{}", str_key), + }), + } + } + + 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), + str_key => Err(Error::NotFound { + what: format!("set live property:{}", str_key), + }), + } + } + + 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 9fdf9704328b13fc39411dd05f0474367e7bdeb5..7c322216fb0ae6db65c60ec2c2e3d711efe2dc18 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 100755 index 0000000000000000000000000000000000000000..95fbd2042b83a45216ec2d5856ee1ec47457c686 --- /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 d336d68b3af7130753f7c2d0a76e6f50b9ac61c6..067f6e80701674c80be0ae0ec53a9765e51d73a1 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 dc0d64ae7eced0435644918ed96c32f901176e21..f48fb09a8250f315adb1400137f308b6d51b7116 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 1e9fb6371f2bc788915613e6fdee0c0a8e6fbeb1..e88db080a7f7fc8b14ab061fbaa1bd91d02c414d 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 1d3cc7ce1f5a05faeefa1527fbefbb8c91aea9ac..11135bc0c58fb1d12bb256f579724dd22084ce5d 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; @@ -58,6 +59,7 @@ pub struct Unit { 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 { @@ -219,6 +245,7 @@ impl Unit { 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 +659,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 +816,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 +897,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)] diff --git a/core/sysmaster/src/unit/entry/unitx.rs b/core/sysmaster/src/unit/entry/unitx.rs index 68af9f3e2cf0351a5e3e390d67f0eb06807cbdff..e88bcae76606cd8a24ec9bb0b4d171daccc3100c 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; @@ -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 6b356f1d32730a5adde66bfc20d3e5781335b18f..94c97e435c8413cb208dfeb6166b7cdc1be91529 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; @@ -38,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; @@ -59,7 +62,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, @@ -291,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 @@ -304,13 +317,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 { @@ -502,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!( @@ -689,6 +702,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 +992,44 @@ impl UnitManager { } } + pub(self) fn start_transient_unit( + &self, + 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 { + log::info!("Failed to parse job mode{}, err: {}", job_mode_str, e); + return Err(Error::InvalidData); + } + + 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 + })?; + } + + self.jm.exec( + &JobConf::new(&unit, 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 +1131,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 +1278,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 +1338,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 +1446,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 de98a5b7c0069a6f41dcb12d061181965fe655d9..3aea0c51579de446585457441ad3c43ae0150d92 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 f91247fa0368e13985978f940ea6ae4b1e269441..ee888f096f2ec25bec1028856a381c49319ebdf0 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(), + str_key => { + return Err(Error::NotFound { + what: format!("set property:{}", str_key), + }) + } + }; + 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 100755 index 0000000000000000000000000000000000000000..3a4a0e0184135a42d1171ecad58f7234bcd2d3fd --- /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 6ae470f9e433bd1bebcc4af815704b0e96a39fbf..eff77c83a43c2605e845f40d3c33d63115ae7925 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/exts/run/Cargo.toml b/exts/run/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..fefef9323d8e819428d659a18509a4703338f6f7 --- /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 0000000000000000000000000000000000000000..29fed199954fa56033138419926f1aecc911220e --- /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 d62f86b582b02c2d13b1907cd41bd57967ec20a6..c8f3be497abcb0d67a5361c7c645ec05190f1a57 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 0000000000000000000000000000000000000000..a8ca71434f66386bab613c959cdc046a4c631343 --- /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/fs.rs b/libs/basic/src/fs.rs index 201173d6d8f89aa4a359a2cdeeff4e41201b888c..11c4efb01b85d9dfe22b1d36a58cf8c7e7959d6f 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" diff --git a/libs/basic/src/lib.rs b/libs/basic/src/lib.rs index e3655f22277e3fea505b26e9bd1a43c3f5a0b421..de0039508378a8a7164f5ba579a268911631c23b 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 536042e91f6b934ad73d7edcaedb66df02a3164d..109cf563d93b631c8d37d5ee2fed4d3c14593995 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 edd9c59d0b35823428a31d672881c81448bde702..c3bff6d58915d1b90f998b34f2a58d29cd42ef3d 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 c806aab5a295ef503b91f6438baf8dfcb498d28b..03aebca2cc78347eea5877f2e8dde442de4b0108 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 47709b3d2a842f3e986fa25397337810989532a1..d2876ee9038a9d34b054f4f3dfba3e598d2a5baf 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 46a3a50a5ac95eff96b7eb19248e3791a7f48200..a23c2901ddc4ef94c87f6a0deeb3d6ca51e01b53 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";