diff --git a/Cargo.lock b/Cargo.lock index 47074ee44dfc88c900ff0816c2b1819986e4cd70..cf164eaeffa0752f33a2a9894958c716c83c2798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -326,6 +326,7 @@ dependencies = [ "basic", "bitflags", "cgroup", + "cmdproto", "confique", "constants", "event", @@ -1917,6 +1918,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/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..06ca203d061c0cf46616e3ea28014c626a939bde 100644 --- a/core/coms/socket/src/rentry.rs +++ b/core/coms/socket/src/rentry.rs @@ -14,13 +14,14 @@ use core::exec::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 = deserialize_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/libcore/Cargo.toml b/core/libcore/Cargo.toml index 933142091465bc0ee10cea015d91ac9c073b6872..1afd6da5de5e852975972a39961ca1fd94467c38 100644 --- a/core/libcore/Cargo.toml +++ b/core/libcore/Cargo.toml @@ -20,6 +20,7 @@ event = { path = "../../libs/event" } log = { path = "../../libs/log" } constants = { path = "../../libs/constants" } unit_parser = { path = "../../libs/unit_parser" } +cmdproto = {path = "../../libs/cmdproto"} # third libraries bitflags = "1.3.2" 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..e6d7c909aa7ced0aafd5495bbb566038772bbf95 100644 --- a/core/libcore/src/unit/base.rs +++ b/core/libcore/src/unit/base.rs @@ -11,10 +11,10 @@ // See the Mulan PSL v2 for more details. use super::super::rel::ReStation; +use super::deps::{UnitType, UnitWriteFlags}; use super::kill::{KillContext, KillOperation}; use super::state::{UnitActiveState, UnitNotifyFlags}; use super::umif::UnitMngUtil; -use super::UnitType; use crate::error::*; use basic::time::UnitTimeStamp; use bitflags::bitflags; @@ -34,6 +34,8 @@ pub trait UnitBase { /// fn id(&self) -> String; /// + fn unit_type(&self) -> UnitType; + /// fn test_start_limit(&self) -> bool; /// fn reset_start_limit(&self); @@ -73,6 +75,21 @@ pub trait UnitBase { /// guess main pid from the cgroup path fn guess_main_pid(&self) -> Result; + /// + fn is_load_stub(&self) -> bool; + + /// + fn transient(&self) -> bool; + + /// + fn transient_file(&self) -> Option; + + /// + fn last_section_private(&self) -> i8; + + /// + fn set_last_section_private(&self, lsp: i8); + /// fn get_unit_timestamp(&self) -> Rc>; } @@ -166,6 +183,13 @@ pub trait SubUnit: ReStation + UnitMngUtil { false } + /// + fn unit_set_property(&self, _key: &str, _value: &str, _flags: UnitWriteFlags) -> Result<()> { + Err(Error::NotFound { + what: "set property".to_string(), + }) + } + // ================ ONLY VALID FOR SERVICE ================ /// fn set_socket_fd(&self, _fd: i32) {} @@ -198,11 +222,11 @@ macro_rules! declare_unitobj_plugin_with_param { #[cfg_attr(feature = "plugin", no_mangle)] pub fn __subunit_create_with_params( um: Rc, - ) -> *mut dyn $crate::unit::SubUnit { + ) -> *const dyn $crate::unit::SubUnit { let construcotr: fn(um: Rc) -> $unit_type = $constructor; let obj = construcotr(um); - let boxed: Box = Box::new(obj); - Box::into_raw(boxed) + let rced: Rc = Rc::new(obj); + Rc::into_raw(rced) } }; } diff --git a/core/libcore/src/unit/deps.rs b/core/libcore/src/unit/deps.rs index 876dfef165cfbfb16eb88acc0f562b142f685834..cfc3919b3593984e70180a52dc59d8b4f7ed048d 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}; @@ -185,6 +186,23 @@ impl TryFrom for UnitType { } } +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) +} + /// parse UnitType by unit_name pub fn unit_name_to_type(unit_name: &str) -> UnitType { let words: Vec<&str> = unit_name.split('.').collect(); diff --git a/core/libcore/src/unit/mod.rs b/core/libcore/src/unit/mod.rs index a043be4bb02fc515d9f9cd1286305c6140e372c3..1c486c38392e2946bbd137868d118f9d70045bc5 100644 --- a/core/libcore/src/unit/mod.rs +++ b/core/libcore/src/unit/mod.rs @@ -12,11 +12,16 @@ //! 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 +29,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..3ae1b3cd8dcf336d6e662c635013655f22c28760 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::SYSMASTER_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(SYSMASTER_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..5b4a865bea9fd8cf5d9e672cb607e27cbd28553c 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::SYSMASTER_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(SYSMASTER_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(SYSMASTER_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::SYSMASTER_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 method_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(SYSMASTER_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(SYSMASTER_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(SYSMASTER_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..a7b2871c476ef4e88488bddbee9c2265840f82b8 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 method_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..48b8fc7161809748920b541012648b32c04fbb52 100644 --- a/core/sysmaster/src/plugin/mod.rs +++ b/core/sysmaster/src/plugin/mod.rs @@ -158,15 +158,19 @@ impl Plugin { let mut ret: String = String::with_capacity(256); #[cfg(test)] let lib_path_devel = { - let devel_path = |out_dir: &str| { - if out_dir.contains("build") { - let _tmp: Vec<_> = out_dir.split("build").collect(); - String::from(_tmp[0]) + let devel_path = |out_dir: Option<&str>| { + if let Some(outdir) = out_dir { + if outdir.contains("build") { + let _tmp: Vec<_> = outdir.split("build").collect(); + String::from(_tmp[0]) + } else { + outdir.to_string() + } } else { - out_dir.to_string() + String::new() } }; - devel_path(env!("OUT_DIR")) + devel_path(option_env!("OUT_DIR")) }; #[cfg(not(test))] @@ -451,7 +455,7 @@ impl Plugin { &self, unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { type FnType = fn(um: Rc) -> *mut dyn SubUnit; let dy_lib = match self.get_lib(unit_type) { @@ -480,8 +484,8 @@ impl Plugin { }; log::debug!("create unit obj with params"); - let boxed_raw = fun(um.clone()); - Ok(unsafe { Box::from_raw(boxed_raw) }) + let rced_raw = fun(um.clone()); + Ok(unsafe { Rc::from_raw(rced_raw) }) } /// Create a obj for subclasses of unit manager /// each sub unit manager need reference of declare_umobj_plugin @@ -575,6 +579,7 @@ mod tests { fn init_test() -> Arc { log::init_log( + "test_plugin", log::Level::Trace, vec!["console", "syslog"], "", @@ -589,8 +594,8 @@ mod tests { fn test_plugin_load_library() { let t_p = init_test(); let mf = env!("CARGO_MANIFEST_DIR"); - let out_dir = env!("OUT_DIR"); - log::info!("{},{}", out_dir, mf); + let out_dir = option_env!("OUT_DIR"); + log::info!("{:?},{}", out_dir, mf); for key in (*t_p.load_libs.read().unwrap()).keys() { // let service_unit = u_box.as_any().downcast_ref::().unwrap(); // assert_eq!(service_unit.get_unit_name(),""); diff --git a/core/sysmaster/src/unit/bus.rs b/core/sysmaster/src/unit/bus.rs new file mode 100755 index 0000000000000000000000000000000000000000..78a2677d2089e2e108a5786a2acebb9175783026 --- /dev/null +++ b/core/sysmaster/src/unit/bus.rs @@ -0,0 +1,284 @@ +// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved. +// +// sysMaster is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::super::job::JobManager; +use super::entry::UnitX; +use super::rentry::{self, UnitLoadState}; +use super::submanager::UnitSubManagers; +use super::uload::UnitLoad; +use basic::fs::RUN_TRANSIENT_PATH; +use core::error::*; +use core::rel::Reliability; +use core::unit::{self, UnitType, UnitWriteFlags}; +use nix::sys::stat::{self, Mode}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::rc::Rc; + +pub struct UnitBus { + // associated objects + reli: Rc, + load: Rc, + jm: Rc, + sms: Rc, + // owned objects +} + +impl UnitBus { + pub(super) fn new( + relir: &Rc, + loadr: &Rc, + jmr: &Rc, + smsr: &Rc, + ) -> UnitBus { + UnitBus { + reli: Rc::clone(relir), + load: Rc::clone(loadr), + jm: Rc::clone(jmr), + sms: Rc::clone(smsr), + } + } + + pub(super) fn transient_unit_from_message( + &self, + properties: &[(&str, &str)], + name: &str, + ) -> Result> { + let unit_type = rentry::unit_name_to_type(name); + if unit_type == UnitType::UnitTypeInvalid { + return Err(Error::InvalidData); + } + + if !self.sms.can_transient(unit_type) { + return Err(Error::InvalidData); + } + + // the first incomplete loading + let unit = match self.load.load_unit(name) { + None => { + return Err(Error::UnitActionENoent); + } + Some(v) => v, + }; + + // prevent duplicate actions + if !self.unit_is_pristine(&unit) { + return Err(Error::UnitActionEAlready); + } + + // set configuration file of the transient unit + self.unit_make_transient(&unit, properties)?; + + // the second real loading + self.load.load_update(&unit); + + Ok(unit) + } + + fn unit_is_pristine(&self, unit: &Rc) -> bool { + let load_state = unit.load_state(); + let load = load_state == UnitLoadState::Loaded || load_state == UnitLoadState::NotFound; + let exist = !load + || !unit.load_paths().is_empty() + || unit.merged_into().is_some() + || self.jm.has_job(unit); + !exist + } + + fn unit_make_transient(&self, unit: &Rc, properties: &[(&str, &str)]) -> Result<()> { + self.reli.set_last_unit(&unit.id()); + let ret = self.unit_make_transient_body(unit, properties); + self.reli.clear_last_unit(); + if ret.is_err() { + unit.remove_transient(); + } + ret + } + + fn unit_make_transient_body( + &self, + unit: &Rc, + properties: &[(&str, &str)], + ) -> Result<()> { + let name = unit.id(); + let path = get_transient_file_path(&name); + + unit.make_transient(Some(path)); // record first + create_transient_file(&name)?; // create file + self.unit_set_properties(unit, properties, UnitWriteFlags::RUNTIME)?; // write file + + Ok(()) + } + + fn unit_set_properties( + &self, + unit: &Rc, + properties: &[(&str, &str)], + flags: UnitWriteFlags, + ) -> Result<()> { + for property in properties { + self.unit_set_property(unit, property.0, property.1, flags)?; + } + + Ok(()) + } + + fn unit_set_property( + &self, + unit: &Rc, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let mut ret = unit.set_sub_property(key, value, flags); + if let Err(Error::NotFound { what: _ }) = ret { + if unit.transient() && unit.load_state() == UnitLoadState::Stub { + ret = self.unit_set_transient_property(unit, key, value, flags); + } + } + + if let Err(Error::NotFound { what: _ }) = ret { + ret = self.unit_set_live_property(unit, key, value, flags); + } + + ret + } + + fn unit_set_transient_property( + &self, + unit: &Rc, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + let ps = self.sms.private_section(unit.unit_type()); + match key { + "RefuseManualStart" + | "RefuseManualStop" + | "DefaultDependencies" + | "OnSuccessJobMode" + | "OnFailureJobMode" + | "IgnoreOnIsolate" + | "JobTimeoutAction" + | "StartLimitBurst" + | "StartLimitAction" + | "FailureAction" + | "SuccessAction" + | "ConditionACPower" + | "ConditionCapability" + | "ConditionDirectoryNotEmpty" + | "ConditionFileIsExecutable" + | "ConditionFileNotEmpty" + | "ConditionFirstBoot" + | "ConditionKernelCommandLine" + | "ConditionNeedsUpdate" + | "ConditionPathExists" + | "ConditionPathExistsGlob" + | "ConditionPathIsDirectory" + | "ConditionPathIsMountPoint" + | "ConditionPathIsReadWrite" + | "ConditionPathIsSymbolicLink" + | "ConditionSecurity" + | "ConditionUser" + | "AssertPathExists" + | "Documentation" => self.unit_write_property(unit, &ps, key, value, flags, false), + "Wants" | "Requires" | "BindsTo" | "Requisite" | "PartOf" | "OnFailure" + | "OnSuccess" | "Before" | "After" | "Conflicts" => { + self.unit_write_property(unit, &ps, key, value, flags, false) + } + 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..1f16200ace6e2fc4ac084caa09facca9fd31518c 100644 --- a/core/sysmaster/src/unit/entry/uentry.rs +++ b/core/sysmaster/src/unit/entry/uentry.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. use super::base::UeBase; +use super::bus::UeBus; use super::cgroup::UeCgroup; use super::child::UeChild; use super::condition::{assert_keys::*, condition_keys::*, UeCondition}; @@ -26,7 +27,7 @@ use basic::time::{now_clockid, UnitTimeStamp}; use cgroup::{self, CgFlags}; use core::error::*; use core::rel::ReStation; -use core::unit::{KillContext, KillMode, KillOperation, UnitNotifyFlags}; +use core::unit::{KillContext, KillMode, KillOperation, UnitNotifyFlags, UnitWriteFlags}; use core::unit::{SubUnit, UnitActiveState, UnitBase, UnitType}; use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; use nix::sys::socket::UnixCredentials; @@ -49,15 +50,16 @@ pub struct Unit { base: Rc, config: Rc, - load: UeLoad, + load: Rc, child: UeChild, cgroup: UeCgroup, conditions: Rc, start_limit: StartLimit, - sub: Box, + sub: Rc, merged_into: RefCell>>, in_stop_when_bound_queue: RefCell, timestamp: Rc>, + bus: UeBus, } impl PartialEq for Unit { @@ -133,6 +135,10 @@ impl UnitBase for Unit { self.id() } + fn unit_type(&self) -> UnitType { + self.unit_type() + } + /*fn get_dependency_list(&self, _unit_name: &str, _atom: libcore::unit::UnitRelationAtom) -> Vec> { todo!() }*/ @@ -192,6 +198,26 @@ impl UnitBase for Unit { fn get_unit_timestamp(&self) -> Rc> { self.get_unit_timestamp() } + + fn is_load_stub(&self) -> bool { + self.load.load_state() == UnitLoadState::Stub + } + + fn transient(&self) -> bool { + self.load.transient() + } + + fn transient_file(&self) -> Option { + self.load.transient_file() + } + + fn last_section_private(&self) -> i8 { + self.load.last_section_private() + } + + fn set_last_section_private(&self, lsp: i8) { + self.load.set_last_section_private(lsp); + } } impl Unit { @@ -202,23 +228,25 @@ impl Unit { dmr: &Rc, rentryr: &Rc, filer: &Rc, - sub: Box, + subr: &Rc, ) -> Rc { let _base = Rc::new(UeBase::new(rentryr, String::from(name), unit_type)); let _config = Rc::new(UeConfig::new(&_base)); + let _load = Rc::new(UeLoad::new(dmr, filer, &_base, &_config)); let _u = Rc::new(Unit { dm: Rc::clone(dmr), base: Rc::clone(&_base), config: Rc::clone(&_config), - load: UeLoad::new(dmr, filer, &_base, &_config), + load: Rc::clone(&_load), child: UeChild::new(&_base), cgroup: UeCgroup::new(&_base), conditions: Rc::new(UeCondition::new()), - sub, + sub: Rc::clone(subr), start_limit: StartLimit::new(), merged_into: RefCell::new(None), in_stop_when_bound_queue: RefCell::new(false), timestamp: Rc::new(RefCell::new(UnitTimeStamp::default())), + bus: UeBus::new(&_config), }); let owner = Rc::clone(&_u); _u.sub.attach_unit(owner); @@ -632,6 +660,7 @@ impl Unit { pub(super) fn load_unit(&self) -> Result<()> { self.set_in_load_queue(false); + self.load.finalize_transient()?; match self.load.load_unit_confs() { Ok(_) => { let paths = self.load.get_unit_id_fragment_pathbuf(); @@ -788,10 +817,26 @@ impl Unit { self.load.load_state() } + pub(super) fn load_paths(&self) -> Vec { + self.load.paths() + } + + pub(super) fn transient(&self) -> bool { + self.load.transient() + } + pub fn set_load_state(&self, state: UnitLoadState) { self.load.set_load_state(state) } + pub(super) fn make_transient(&self, path: Option) { + self.load.make_transient(path) + } + + pub(super) fn remove_transient(&self) { + self.load.remove_transient() + } + pub(super) fn child_add_pids(&self, pid: Pid) { self.child.add_pids(pid); } @@ -853,6 +898,19 @@ impl Unit { pub fn get_unit_timestamp(&self) -> Rc> { Rc::clone(&self.timestamp) } + + pub(crate) fn set_sub_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + self.sub.unit_set_property(key, value, flags) + } + + pub(crate) fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.bus.set_property(key, value) + } } #[cfg(test)] @@ -898,7 +956,7 @@ mod tests { &Rc::new(dm), &rentry, &Rc::new(unit_file), - sub_obj, + &sub_obj, ) } diff --git a/core/sysmaster/src/unit/entry/unitx.rs b/core/sysmaster/src/unit/entry/unitx.rs index 68af9f3e2cf0351a5e3e390d67f0eb06807cbdff..67a93389040d4dff12993ff1dbc82537e91f4133 100644 --- a/core/sysmaster/src/unit/entry/unitx.rs +++ b/core/sysmaster/src/unit/entry/unitx.rs @@ -20,9 +20,10 @@ use crate::unit::util::UnitFile; use basic::IN_SET; use core::error::*; use core::rel::ReStation; -use core::unit::{SubUnit, UnitActiveState, UnitRelations, UnitType}; +use core::unit::{self, SubUnit, UnitActiveState, UnitRelations, UnitType, UnitWriteFlags}; use nix::sys::wait::WaitStatus; use nix::unistd::Pid; +use std::fmt::Arguments; use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; @@ -62,9 +63,9 @@ impl UnitX { filer: &Rc, unit_type: UnitType, name: &str, - subclass: Box, + subclass: Rc, ) -> UnitX { - let unit = Unit::new(unit_type, name, dmr, rentryr, filer, subclass); + let unit = Unit::new(unit_type, name, dmr, rentryr, filer, &subclass); UnitX(unit) } @@ -246,10 +247,26 @@ impl UnitX { self.0.load_state() } + pub(crate) fn load_paths(&self) -> Vec { + self.0.load_paths() + } + + pub(crate) fn transient(&self) -> bool { + self.0.transient() + } + pub(crate) fn set_load_state(&self, state: UnitLoadState) { self.0.set_load_state(state) } + pub(crate) fn make_transient(&self, path: Option) { + self.0.make_transient(path) + } + + pub(crate) fn remove_transient(&self) { + self.0.remove_transient() + } + pub(crate) fn unit_type(&self) -> UnitType { self.0.unit_type() } @@ -274,6 +291,30 @@ impl UnitX { self.0.child_remove_pids(pid); } + pub(crate) fn set_sub_property( + &self, + key: &str, + value: &str, + flags: UnitWriteFlags, + ) -> Result<()> { + self.0.set_sub_property(key, value, flags) + } + + pub(crate) fn set_property(&self, key: &str, value: &str) -> Result<()> { + self.0.set_property(key, value) + } + + pub(crate) fn write_settingf( + &self, + ps: &str, + flags: UnitWriteFlags, + name: &str, + args: Arguments<'_>, + ) -> Result<()> { + let unit = self.unit(); + unit::unit_write_settingf(unit, ps, flags, name, args) + } + pub(crate) fn unit(&self) -> Rc { Rc::clone(&self.0) } diff --git a/core/sysmaster/src/unit/manager.rs b/core/sysmaster/src/unit/manager.rs index 6b356f1d32730a5adde66bfc20d3e5781335b18f..e7ff6cab251e740588e601e07a099cd7141db2f2 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 { @@ -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,48 @@ impl UnitManager { } } + fn transient_unit(&self, unit_config: &UnitConfig) -> Result> { + let mut unit_properties: Vec<(&str, &str)> = vec![]; + for p in &unit_config.unit_properties { + unit_properties.push((&p.key, &p.value)); + } + self.bus + .transient_unit_from_message(&unit_properties, &unit_config.unit_name) + } + + 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.transient_unit(unit_config).map_err(|e| { + log::info!("Failed to get transient unit with err: {}", e); + e + })?; + + for unit_config in aux_units { + self.transient_unit(unit_config).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 +1135,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 +1282,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 +1342,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 +1450,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/core/sysmaster/src/unit/util/unit_om.rs b/core/sysmaster/src/unit/util/unit_om.rs index 476448c01b65fc7cadb91d64cfcf278722c5d78e..64842b3f575df0e6334f590003b2abc1b1ee7525 100644 --- a/core/sysmaster/src/unit/util/unit_om.rs +++ b/core/sysmaster/src/unit/util/unit_om.rs @@ -20,7 +20,7 @@ pub(crate) fn create_um_obj( pub(crate) fn create_subunit_with_um( unit_type: UnitType, um: Rc, -) -> Result> { +) -> Result> { #[cfg(feature = "noplugin")] return noplugin::create_subunit_with_um(unit_type, um); #[cfg(feature = "plugin")] @@ -47,7 +47,7 @@ mod plugin { pub(super) fn create_subunit_with_um( unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { Plugin::get_instance().create_subunit_with_um(unit_type, um) } } @@ -106,7 +106,7 @@ mod noplugin { pub(super) fn create_subunit_with_um( unit_type: UnitType, um: Rc, - ) -> Result> { + ) -> Result> { let fun = match unit_type { #[cfg(feature = "mount")] UnitType::UnitMount => mount::__subunit_create_with_params, @@ -127,6 +127,6 @@ mod noplugin { } }; let boxed_raw = fun(um.clone()); - Ok(unsafe { Box::from_raw(boxed_raw) }) + Ok(unsafe { Rc::from_raw(boxed_raw) }) } } diff --git a/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..5ec6c4d878d1951bd98a8f20a2b1f04d85f80237 --- /dev/null +++ b/exts/run/src/main.rs @@ -0,0 +1,665 @@ +// 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::SYSMASTER_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 = "simple")] + 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(); + + 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 unit_properties; + let aux_name; + let mut 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_service_set_properties(&args)?; + aux_unit_properties = + transient_unit_set_properties(&args.description, args.collect, &args.timer_property)?; + aux_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(SYSMASTER_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..e0db4cf0f16d5747a42f34bd29891502cb5e0528 100644 --- a/libs/basic/Cargo.toml +++ b/libs/basic/Cargo.toml @@ -70,6 +70,7 @@ full = [ "namespace", "time", "path", + "env", "glob", ] @@ -124,4 +125,5 @@ unit_name = [] uuid = ["bitflags", "random"] time = [] path = [] +env = [] glob = [] 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..d8635ac85a04452076b99ed656cfb4724924a010 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 method_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.method_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..0323779e87ed90e5733cade7f94f49ba84490254 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 SYSMASTER_SOCKET: &str = "/run/sysmaster/sysmaster_socket"; /// Default log file path when LogTarget is configured to "file" pub const LOG_FILE_PATH: &str = "/var/log/sysmaster/sysmaster.log";