use ciborium::Value as CborValue; use serde::Deserialize; use std::env::VarError; use std::fs; use tracing::{debug, error, info, trace, warn}; use clap::ArgMatches; use std::path::{Path, PathBuf}; use std::time::UNIX_EPOCH; use sha2::{Sha256, Sha512, Digest}; use flate2::bufread::GzDecoder; use tar::Archive; use std::fs::File; use std::io::copy; use crate::id::MizeId; 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 crate::{mize_err, Module}; use crate::instance::module::EmptyModule; use self::fsstore::FileStore; pub mod fsstore; #[cfg(target_family = "unix")] mod unix_socket; #[cfg(target_family = "unix")] use crate::platform::os::unix_socket::UnixListener; //mod web; pub fn os_instance_init(instance: &mut 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 match std::env::var("MIZE_CONFIG_FILE") { Ok(config_file_path) => { let config = config_from_file(config_file_path)?; debug!("env var MIZE_CONFIG_FILE present"); instance.set_blocking("0", config)?; trace!("config after MIZE_CONFIG_FILE env var: {}", instance.get("0/config")?); } Err(var_err) => match var_err { VarError::NotPresent => { debug!("env var MIZE_CONFIG_FILE NOT present"); }, VarError::NotUnicode(_) => { warn!("env var MIZE_CONFIG_FILE is not Unicode, so not reading it") }, } }; ////// load config from MIZE_CONFIG env var match std::env::var("MIZE_CONFIG") { Ok(config_string) => { let config = config_from_string(config_string)?; debug!("env var MIZE_CONFIG present"); instance.set_blocking("0", config)?; trace!("config after MIZE_CONFIG env var: {}", instance.get("0/config")?); } Err(var_err) => match var_err { VarError::NotPresent => { debug!("env var MIZE_CONFIG NOT present") }, VarError::NotUnicode(_) => { warn!("env var MIZE_CONFIG is not Unicode, so not reading it") }, } }; ////// if a config.store_path is set, upgrade to the filestore there let mut test = instance.get("0")?.as_data_full()?; let mut store_path = match instance.get("0/config/store_path")?.value_string() { Ok(path) => { path }, Err(e) => { // the default store_path: $HOME/.mize let home_dir = env!("HOME"); if home_dir == "" { return Err(mize_err!("env var $HOME empty")); } let store_path = home_dir.to_owned() + "/.mize"; instance.set_blocking("0/config/store_path", store_path.clone().into_item_data())?; let store_path = instance.get("0/config/store_path")?.value_string()?; println!("store_path: {}", store_path); store_path }, }; if FileStore::store_is_opened(store_path.to_owned())? { // if the store is already opened, connect to the instance, that opened it and join // it's namespace #[cfg(target_family = "unix")] { info!("CONNECTING"); use crate::platform::os::unix_socket; unix_socket::connect(instance, store_path.into())?; return Ok(()); } #[cfg(target_family = "windows")] { warn!("CONNECTING... would connect, but that is not implemented on windows yet"); } } else { // else open it ourselves let file_store = FileStore::new(store_path.as_str())?; instance.migrate_to_store(Box::new(file_store))?; let path = Path::new(&store_path).to_owned(); #[cfg(target_family = "unix")] { instance.add_listener(UnixListener::new(path)?)?; } #[cfg(target_family = "windows")] { warn!("would add a Listener on a local socket, but that is not yet implemented for windows");; } } Ok(()) } pub fn seconds_since_modification(path: &Path) -> MizeResult { let metadata = fs::metadata(path)?; let modified = metadata.modified()?; let duration = modified.duration_since(UNIX_EPOCH)?; Ok(duration.as_secs()) } pub fn get_module_hash(instance: &mut Instance, name: &str, mut options: ItemData) -> MizeResult<(String, String)> { let mut selector_data = instance.get("0/config/selector")?.as_data_full()?; selector_data.set_path("modName", name)?; selector_data.merge(options); selector_data.sort_keys(); let selector_str = selector_data.to_json()?; let mut hasher = Sha256::new(); hasher.update(selector_str.clone().as_bytes()); let result = hasher.finalize(); let mut hex: String = result.iter() .map(|b| format!("{:02x}", b).to_string()) .collect::>() .join(""); hex.truncate(32); return Ok((hex, selector_str)); } fn download_module(instance: &mut Instance, store_path: &str, module_url: &str, module_hash: &str, module_name: &str, module_selector: &str) -> MizeResult<()> { let tmp_file_path_gz = format!("{}/{}.tar.gz", store_path, module_hash); let tmp_gz_file = if !PathBuf::from(tmp_file_path_gz.clone()).exists() { let mut tmp_gz_file = File::create(&tmp_file_path_gz)?; debug!("downloading module '{}' with hash: {}", module_name, module_hash); let response = http_req::request::get(format!("http://{}/mize/dist/{}-{}.tar.gz", module_url, module_hash, module_name), &mut tmp_gz_file)?; let code = response.status_code(); if code != 200.into() { return Err(mize_err!("failed to get module '{}' with hash '{}' and selector: {}", module_name, module_hash, module_selector).msg(format!("the http request to fetch the module returned a status code of: {}", code))); } tmp_gz_file } else { std::fs::File::open(tmp_file_path_gz.clone())? }; let tar = GzDecoder::new(std::io::BufReader::new(tmp_gz_file)); let mut archive = Archive::new(tar); //archive.set_mask(umask::Mode::parse("rwxrwxrwx")?.into()); let target_dir = format!("{}/modules/{}", store_path, module_hash); std::fs::create_dir_all(target_dir.as_str())?; archive.unpack(target_dir.as_str())?; fs::remove_file(&tmp_file_path_gz)?; Ok(()) } pub fn fetch_module(instance: &mut Instance, module_name: &str) -> MizeResult { if let Ok(module_path) = instance.get(format!("0/config/module_dir/{}", module_name))?.value_string() { return Ok(module_path); } else { // if null, load it from the url let module_url = instance.get("0/config/module_url")?.value_string()?; let store_path = instance.get("0/config/store_path")?.value_string()?; let (module_hash, module_selector) = get_module_hash(instance, module_name, ItemData::new())?; if !PathBuf::from(format!("{}/modules/{}", store_path, module_hash.as_str())).exists() { // download module if it does not exist download_module(instance, store_path.as_str(), module_url.as_str(), module_hash.as_str(), module_name, module_selector.as_str())?; } return Ok(format!("{}/modules/{}", store_path, module_hash.as_str())); } } pub fn load_module(instance: &mut Instance, module_name: &str, path: Option) -> MizeResult<()> { let module_path = format!("{}/lib/libmmize_module_{}.so", fetch_module(instance, module_name)?, module_name); let lib = unsafe { libloading::Library::new(module_path)? }; let func: libloading::Symbol) -> ()> = unsafe { lib.get(format!("get_mize_module_{}", module_name).as_bytes())? }; let mut module: Box = Box::new(EmptyModule {}); unsafe { func(&mut module) }; let mut modules_inner = instance.modules.lock()?; module.init(&instance)?; modules_inner.insert(module_name.to_owned(), module); unsafe { // dropping the lib, would (i suspect) free all memory, of the library's code, which would // make the module's vtable point into empty memory std::mem::forget(lib); } Ok(()) } pub fn config_from_cli_args(matches: &ArgMatches) -> MizeResult { let mut config = ItemData::new(); if let Some(config_file_path) = matches.get_one::("config-file") { config.merge(config_from_file(config_file_path.to_string())?); trace!("config after --config-file arg: {}", config); } if let Some(config_string) = matches.get_one::("config") { config.merge(config_from_string(config_string.to_string())?); trace!("config after --config arg: {}", config); } return Ok(config); } pub fn config_from_file(file_path: String) -> MizeResult { let toml_string = fs::read_to_string(&file_path) .mize_result_msg(format!("Could not read file {} as mize config", &file_path))?; let item_data = ItemData::from_toml(toml_string.as_str())?; return Ok(item_data); } fn config_from_string(config_string: String) -> MizeResult { let mut config = ItemData::new(); for option in config_string.split(":") { if option == "".to_owned() { continue; } let path = option.split("=").nth(0) .ok_or(MizeError::new().msg(format!("Failed to parse Option: option '{}' has an empty path (thing beforee =)", option)))?; let value = option.split("=").nth(1) .ok_or(MizeError::new().msg(format!("Failed to parse Option: option '{}' has an empty value (thing after =)", option)))?; let mut path_vec = vec!["config"]; path_vec.extend(path.split(".")); config.set_path(path_vec, ItemData::parse(value))?; } return Ok(config); }