diff --git a/Cargo.lock b/Cargo.lock index 8a7eeb3..b924521 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1068,6 +1068,7 @@ dependencies = [ "nix", "serde", "serde_json", + "sysinfo", "tokio", "toml", "uuid", @@ -1105,6 +1106,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1583,6 +1593,21 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sysinfo" +version = "0.30.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -2257,6 +2282,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.5", +] + [[package]] name = "windows-core" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 6088751..982430a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ path = "src/platform/os/main.rs" required-features = [ "os-target" ] [features] -os-target = [ "clap", "home", "env_logger", "daemonize", "nix", "toml", "serde_json" ] +os-target = [ "clap", "home", "env_logger", "daemonize", "nix", "toml", "serde_json", "sysinfo", "ciborium/default"] wasm-target = [ "wasm-bindgen", "console_error_panic_hook", "wasm-bindgen-cli" ] @@ -44,6 +44,7 @@ daemonize = { version = "0.5.0", optional = true } nix = { version = "0.27.1", features = ["signal"], optional = true } toml = { version = "0.8.13", features = ["parse"], optional = true } serde_json = { version = "1.0.117", optional = true } +sysinfo = { version = "0.30.12", optional = true } # bare dependencies diff --git a/src/core/error.rs b/src/core/error.rs index a154f78..f0aff00 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -7,6 +7,11 @@ use std::io; use colored::Colorize; use log::{trace, debug, info, warn, error}; +#[macro_export] +macro_rules! mize_err { + ($($arg:tt)*) => { MizeError::new().msg(format!( $($arg)*)) }; +} + use crate::proto::MizeMessage; pub type MizeResult = Result; diff --git a/src/core/instance/mod.rs b/src/core/instance/mod.rs index ba07242..a248743 100644 --- a/src/core/instance/mod.rs +++ b/src/core/instance/mod.rs @@ -130,7 +130,19 @@ impl Instance { //fn from(value: T) -> Self { //} //} + pub fn migrate_to_store(&mut self, new_store: New) -> MizeResult> { + let old_store = self.store; + for id in old_store.id_iter()? { + let data = self.store.get_value_data_full(self.id_from_string(id))?; + + let id_of_new_store = new_store.new_id()?; + new_store.set(self.id_from_string(id_of_new_store), data.to_owned())?; + }; + + self.store = new_store as S; + Ok(Instance { store: new_store, subs: self.subs, peers: self.peers, id_pool: self.id_pool }) + } } impl fmt::Debug for Instance { diff --git a/src/core/instance/store.rs b/src/core/instance/store.rs index 6ce202d..ef4d440 100644 --- a/src/core/instance/store.rs +++ b/src/core/instance/store.rs @@ -1,6 +1,12 @@ + +use std::iter::Map; +use std::option::Iter; + use crate::error::MizeResult; use crate::item::{Item, ItemData}; use crate::id::MizeId; +use crate::memstore::MemStore; +use crate::instance::Instance; pub trait Store { // should have the ability to be multi threaded, if the underlying implementation supports @@ -23,5 +29,7 @@ pub trait Store { fn get_value_raw(&self, id: MizeId) -> MizeResult>; fn get_value_data_full(&self, id: MizeId) -> MizeResult; + + fn id_iter(&self) -> MizeResult + '_>; } diff --git a/src/core/item.rs b/src/core/item.rs index 1661456..e7aacdd 100644 --- a/src/core/item.rs +++ b/src/core/item.rs @@ -41,6 +41,11 @@ impl Item<'_, S> { // this will call from the instance which gets the value from the store self.instance.store.get_value_raw(self.id()) } + pub fn value_string(&self) -> MizeResult { + let raw = self.value_raw()?; + String::from_utf8(raw.clone()) + .mize_result_msg(format!("failed to convert raw value from item '{}' into a utf-8 string. raw value was: '{:02X?}'", self.id(), raw)) + } pub fn as_data_full(&self) -> MizeResult { self.instance.store.get_value_data_full(self.id()) } @@ -73,6 +78,10 @@ impl ItemData { item_data_merge(&mut self.0, &other.0); } + pub fn cbor(&self) -> &CborValue { + &self.0 + } + pub fn null() -> CborValue { CborValue::Null } @@ -106,7 +115,7 @@ impl ItemData { } -fn item_data_get_path(data: &CborValue, path: Vec) -> MizeResult<&CborValue> { +pub fn item_data_get_path(data: &CborValue, path: Vec) -> MizeResult<&CborValue> { let mut path_iter = path.clone().into_iter(); let path_el = match path_iter.nth(0) { Some(val) => val, @@ -133,7 +142,7 @@ fn item_data_get_path(data: &CborValue, path: Vec) -> MizeResult<&CborVa item_data_get_path(sub_data, path_iter.collect()) } -fn item_data_set_path(data: &mut CborValue, path: Vec, data_to_set: &CborValue) -> MizeResult<()> { +pub fn item_data_set_path(data: &mut CborValue, path: Vec, data_to_set: &CborValue) -> MizeResult<()> { //trace!("[ {} ] item_data_set_path()", "CALL".yellow()); //trace!("[ {} ] data: {}", "ARG".yellow(), data.clone().into_item_data()); //trace!("[ {} ] path: {:?}", "ARG".yellow(), path); @@ -177,7 +186,7 @@ fn item_data_set_path(data: &mut CborValue, path: Vec, data_to_set: &Cbo return Err(MizeError::new().msg("unreachable")); } -fn item_data_merge(merge_into: &mut CborValue, other: &CborValue){ +pub fn item_data_merge(merge_into: &mut CborValue, other: &CborValue){ // needs to be recursive match (merge_into, other) { diff --git a/src/core/memstore.rs b/src/core/memstore.rs index 1ee45c5..a631918 100644 --- a/src/core/memstore.rs +++ b/src/core/memstore.rs @@ -10,6 +10,7 @@ use crate::id::MizeId; use crate::instance::store::Store; use crate::error::{MizeError, MizeResult, IntoMizeResult}; use crate::item::{Item, ItemData}; +use crate::instance::Instance; #[derive(Clone, Debug)] pub struct MemStore { @@ -79,6 +80,12 @@ impl Store for MemStore { return Ok(ret_data); } + fn id_iter(&self) -> MizeResult + '_> { + let inner = self.inner.lock()?; + + let keys: Vec = inner.map.keys().map(|v| format!("{}", v)).collect(); + return Ok(keys.into_iter()); + } } impl MemStore { @@ -94,7 +101,7 @@ fn id_to_u64(id: MizeId) -> MizeResult { .mize_result_msg(format!("Could not parse the store_part of mizeid {} into a u64 for the MemStore", id)) } -fn get_raw_from_cbor<'a>(value: &'a CborValue, path: Vec<&String>) -> MizeResult<&'a [u8]> { +pub fn get_raw_from_cbor<'a>(value: &'a CborValue, path: Vec<&String>) -> MizeResult<&'a [u8]> { trace!("[ {} ] get_raw_from_cbor()", "CALL".yellow()); trace!("[ {} ] value: {:?}", "ARG".yellow(), value); trace!("[ {} ] path: {:?}", "ARG".yellow(), path); diff --git a/src/platform/os/fsstore.rs b/src/platform/os/fsstore.rs index e69de29..9ccc189 100644 --- a/src/platform/os/fsstore.rs +++ b/src/platform/os/fsstore.rs @@ -0,0 +1,132 @@ +use std::fs; +use std::path::{Path, PathBuf}; +use std::fs::OpenOptions; +use sysinfo::{System, Pid, ProcessRefreshKind, RefreshKind}; +use std::io::Write; +use ciborium::Value as CborValue; + +use crate::item::IntoItemData; +use crate::memstore::MemStore; +use crate::{core::instance::store::Store, mize_err}; +use crate::error::{IntoMizeResult, MizeResult, MizeError}; +use crate::core::item::{ItemData, Item}; +use crate::core::id::MizeId; +use crate::core::memstore::get_raw_from_cbor; +use crate::instance::Instance; + +pub struct FileStore { + path: PathBuf, +} + +impl FileStore { + pub fn new(path: String) -> MizeResult { + + // create that path if it does not exist + fs::create_dir_all(Path::new(&path).join("store"))?; + + // check for valid pid file + if let Some(pid) = valid_pid_file(&path)? { + // store already opened + return Err(mize_err!("MizeStore at path {} is already opened by process with pid {}", &path, pid)); + + } else { + // write our own pid file + let pid = std::process::id(); + let mut file = OpenOptions::new().write(true).create(true).open(&path)?; + write!(file, "{}", pid)?; + } + + Ok(FileStore { path: Path::new(&path).to_owned() }) + } +} + +impl Store for FileStore { + fn new_id(&self) -> MizeResult { + + let mut next_id: u64 = String::from_utf8(fs::read(self.path.join("next_id")) + .mize_result_msg(format!("could not read next_id at '{}'", self.path.display()))?)? + .parse() + .mize_result_msg(format!("could not parse next_id at '{}' to u64", self.path.display()))?; + + let id_string = format!("{}", next_id); + + next_id += 1; + + fs::write(self.path.join("next_id"), format!("{}", next_id))?; + + return Ok(id_string); + } + + fn set>(&self, id: MizeId, data: T) -> MizeResult<()> { + let file = OpenOptions::new().write(true).create(true).open(self.path.join("store").join(id.store_part()))?; + ciborium::into_writer(data.into().cbor(), file)?; + Ok(()) + } + + fn get_links(&self, item: Item) -> MizeResult> { + Ok(Vec::new()) + } + + fn get_backlinks(&self, item: Item) -> MizeResult> { + Ok(Vec::new()) + } + + fn get_value_raw(&self, id: MizeId) -> MizeResult> { + let file = OpenOptions::new().read(true).create(true).open(self.path.join("store").join(id.store_part()))?; + + let cbor_val: CborValue = ciborium::from_reader(file) + .mize_result_msg(format!("could not read file '{}' from FileStore", self.path.join("store").join(id.store_part()).display()))?; + + let path = id.path(); + let mut id_iter = path.iter(); + id_iter.next(); + let id_without_first = id_iter.collect(); + + Ok(get_raw_from_cbor(&cbor_val, id_without_first)?.to_owned()) + } + + fn get_value_data_full(&self, id: MizeId) -> MizeResult { + let file = OpenOptions::new().read(true).create(true).open(self.path.join("store").join(id.store_part()))?; + + let cbor_value: CborValue = ciborium::from_reader(file) + .mize_result_msg(format!("could not read file '{}' from FileStore", self.path.join("store").join(id.store_part()).display()))?; + let data: ItemData = cbor_value.into_item_data(); + + let tmp = id.path(); + let mut path_iter = tmp.into_iter(); + path_iter.next(); + let new_path: Vec = path_iter.map(|v| v.to_owned()).collect(); + let ret_data = data.get_path(new_path)?; + + return Ok(ret_data); + } + fn id_iter(&self) -> MizeResult + '_> { + Ok(vec!["1".to_owned(), "2".to_owned()].into_iter()) + } +} + +fn valid_pid_file(path: &String) -> MizeResult> { + + let pid_file_path = Path::new(path).join("pid"); + + // if the pid file does not exist, there is no pid... + if !pid_file_path.exists() { + return Ok(None); + } + + // read pid file + let pid: u32 = String::from_utf8(fs::read(&pid_file_path) + .mize_result_msg(format!("Could not read contents of pid file at '{}'", pid_file_path.display()))?)? + .parse() + .mize_result_msg(format!("Could not parse contents of pid file at '{}' to u32", pid_file_path.display()))?; + + let refresh_kind = RefreshKind::new().with_processes(ProcessRefreshKind::everything()); + let system = sysinfo::System::new_with_specifics(refresh_kind); + + if let Some(_) = system.process(Pid::from(pid as usize)) { + return Ok(Some(pid)); + } else { + return Ok(None); + } +} + diff --git a/src/platform/os/mod.rs b/src/platform/os/mod.rs index 450b208..36d9ef3 100644 --- a/src/platform/os/mod.rs +++ b/src/platform/os/mod.rs @@ -10,9 +10,16 @@ use crate::instance::store::Store; use crate::instance::Instance; use crate::error::{IntoMizeResult, MizeError, MizeResult}; use crate::item::{ItemData, IntoItemData}; +use crate::memstore::MemStore; + +use self::fsstore::FileStore; + +mod fsstore; +//mod web; +//mod unix-socket; -pub fn os_instance_init(instance: &mut Instance) -> MizeResult<()> { +pub fn os_instance_init(instance: Instance) -> MizeResult> { // this is the code, that runs to initialize an Instance on a system with an os present. ////// load MIZE_CONFIG_FILE as the instance, which is item 0 @@ -51,7 +58,14 @@ pub fn os_instance_init(instance: &mut Instance) -> MizeResult<()> } }; + ////// if a config.store is set, upgrade to the filestore there + let store_path = instance.get("0/conifg/store")?.value_string()?; + if store_path != "" { + let file_store = FileStore::new(store_path)?; + let new_instance = &mut instance.migrate_to_store(file_store)?; + return Ok(new_instance); + } return Ok(()); }