diff --git a/Cargo.lock b/Cargo.lock index 665f508..e7851b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1365,9 +1365,11 @@ dependencies = [ "strum_macros", "tempfile", "term 1.1.0", + "termsize", "thiserror 1.0.69", "tokio", "tokio-stream", + "tokio-util", "tower", "tower-http", "utoipa", @@ -2424,6 +2426,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termsize" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f11ff5c25c172608d5b85e2fb43ee9a6d683a7f4ab7f96ae07b3d8b590368fd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.69" diff --git a/Cargo.toml b/Cargo.toml index 48617e4..8dfc001 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,9 +48,11 @@ stderrlog = "0.6.0" strum = { version = "0.27.2", features = ["derive"] } strum_macros = "0.27.2" term = "1.1.0" +termsize = "0.1.9" thiserror = "1.0" tokio = { version = "1.0", features = ["full"] } tokio-stream = "0.1" +tokio-util = "0.7.16" tower = "0.5.2" tower-http = { version = "0.6.6", features = ["cors", "fs", "trace"] } utoipa = { version = "5.4.0", features = ["axum_extras"] } diff --git a/src/meta_plugin/system.rs b/src/meta_plugin/system.rs deleted file mode 100644 index f33d475..0000000 --- a/src/meta_plugin/system.rs +++ /dev/null @@ -1,862 +0,0 @@ -use gethostname::gethostname; -use std::env; -use std::process; -use crate::meta_plugin::MetaPlugin; - -#[derive(Debug, Clone, Default)] -pub struct CwdMetaPlugin { - meta_name: String, - is_finalized: bool, - base: crate::meta_plugin::BaseMetaPlugin, -} - -impl CwdMetaPlugin { - pub fn new( - options: Option>, - outputs: Option>, - ) -> CwdMetaPlugin { - let mut base = crate::meta_plugin::BaseMetaPlugin::new(); - - // Set default outputs - let default_outputs = vec!["cwd".to_string()]; - for output_name in default_outputs { - base.outputs.insert(output_name.clone(), serde_yaml::Value::String(output_name)); - } - - // Apply provided options and outputs - if let Some(opts) = options { - for (key, value) in opts { - base.options.insert(key, value); - } - } - if let Some(outs) = outputs { - for (key, value) in outs { - base.outputs.insert(key, value); - } - } - - CwdMetaPlugin { - meta_name: "cwd".to_string(), - is_finalized: false, - base, - } - } - - pub fn new_simple() -> CwdMetaPlugin { - Self::new(None, None) - } -} - -impl MetaPlugin for CwdMetaPlugin { - fn is_finalized(&self) -> bool { - self.is_finalized - } - - fn set_finalized(&mut self, finalized: bool) { - self.is_finalized = finalized; - } - - fn finalize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - // Mark as finalized - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - } - } - - fn meta_name(&self) -> String { - self.meta_name.clone() - } - - fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - let mut metadata = Vec::new(); - let cwd = match env::current_dir() { - Ok(path) => path.to_string_lossy().to_string(), - Err(_) => "unknown".to_string(), - }; - - // Use process_metadata_outputs to handle output mapping - if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs( - "cwd", - cwd, - self.base.outputs() - ) { - metadata.push(meta_data); - } - - crate::meta_plugin::MetaPluginResponse { - metadata, - is_finalized: false, - } - } - - fn outputs(&self) -> &std::collections::HashMap { - self.base.outputs() - } - - fn outputs_mut(&mut self) -> &mut std::collections::HashMap { - self.base.outputs_mut() - } - - fn default_outputs(&self) -> Vec { - vec!["cwd".to_string()] - } - - fn default_options(&self) -> std::collections::HashMap { - std::collections::HashMap::new() - } - - fn options(&self) -> &std::collections::HashMap { - self.base.options() - } - - fn options_mut(&mut self) -> &mut std::collections::HashMap { - self.base.options_mut() - } -} - -#[derive(Debug, Clone, Default)] -pub struct UserMetaPlugin { - base: crate::meta_plugin::BaseMetaPlugin, -} - -impl UserMetaPlugin { - pub fn new( - options: Option>, - outputs: Option>, - ) -> UserMetaPlugin { - let mut base = crate::meta_plugin::BaseMetaPlugin::new(); - base.meta_name = "user".to_string(); - - // Initialize with helper function - base.initialize_plugin( - &["user_uid", "user_gid", "user_name", "user_group"], - options, - outputs, - ); - - UserMetaPlugin { - base, - } - } - - pub fn new_simple() -> UserMetaPlugin { - Self::new(None, None) - } - - fn get_current_username() -> Option { - uzers::get_user_by_uid(uzers::get_current_uid()) - .map(|user| user.name().to_string_lossy().to_string()) - } - - fn get_current_groupname() -> Option { - uzers::get_group_by_gid(uzers::get_current_gid()) - .map(|group| group.name().to_string_lossy().to_string()) - } -} - -impl MetaPlugin for UserMetaPlugin { - fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - let mut metadata = Vec::new(); - - // Get user info - let uid = uzers::get_current_uid().to_string(); - let gid = uzers::get_current_gid().to_string(); - let username = Self::get_current_username().unwrap_or_else(|| "unknown".to_string()); - let groupname = Self::get_current_groupname().unwrap_or_else(|| "unknown".to_string()); - - // Process each output - let values = [ - ("user_uid", uid), - ("user_gid", gid), - ("user_name", username), - ("user_group", groupname), - ]; - - for (name, value) in values { - if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs( - name, - value, - self.base.outputs() - ) { - metadata.push(meta_data); - } - } - - crate::meta_plugin::MetaPluginResponse { - metadata, - is_finalized: true, - } - } - - fn meta_name(&self) -> String { - self.base.meta_name.clone() - } - - fn outputs(&self) -> &std::collections::HashMap { - self.base.outputs() - } - - fn outputs_mut(&mut self) -> &mut std::collections::HashMap { - self.base.outputs_mut() - } - - fn default_outputs(&self) -> Vec { - vec!["user_uid".to_string(), "user_gid".to_string(), "user_name".to_string(), "user_group".to_string()] - } - - fn default_options(&self) -> std::collections::HashMap { - std::collections::HashMap::new() - } - - fn options(&self) -> &std::collections::HashMap { - self.base.options() - } - - fn options_mut(&mut self) -> &mut std::collections::HashMap { - self.base.options_mut() - } -} - -#[derive(Debug, Clone, Default)] -pub struct ShellMetaPlugin { - meta_name: String, - is_finalized: bool, - outputs: std::collections::HashMap, - options: std::collections::HashMap, -} - -impl ShellMetaPlugin { - pub fn new( - _options: Option>, - outputs: Option>, - ) -> ShellMetaPlugin { - // Start with default options - let mut final_options = std::collections::HashMap::new(); - if let Some(opts) = _options { - for (key, value) in opts { - final_options.insert(key, value); - } - } - - // Start with default outputs - let mut final_outputs = std::collections::HashMap::new(); - let default_outputs = Self::default().default_outputs(); - for output_name in default_outputs { - final_outputs.insert(output_name.clone(), serde_yaml::Value::String(output_name)); - } - if let Some(outs) = outputs { - for (key, value) in outs { - final_outputs.insert(key, value); - } - } - - ShellMetaPlugin { - meta_name: "shell".to_string(), - is_finalized: false, - outputs: final_outputs, - options: final_options, - } - } - - pub fn new_simple() -> ShellMetaPlugin { - Self::new(None, None) - } -} - -impl MetaPlugin for ShellMetaPlugin { - fn is_finalized(&self) -> bool { - self.is_finalized - } - - fn set_finalized(&mut self, finalized: bool) { - self.is_finalized = finalized; - } - - fn finalize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - // Mark as finalized - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - } - } - - fn update(&mut self, _data: &[u8]) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process more data - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: false, - } - } - - fn meta_name(&self) -> String { - self.meta_name.clone() - } - - fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - let mut metadata = Vec::new(); - let shell = match env::var("SHELL") { - Ok(shell) => shell, - Err(_) => "unknown".to_string(), - }; - - // Use process_metadata_outputs to handle output mapping - if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs( - "shell", - shell, - &self.outputs - ) { - metadata.push(meta_data); - } - - // Mark as finalized since this plugin only needs to run once - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata, - is_finalized: true, - } - } - - fn outputs(&self) -> &std::collections::HashMap { - &self.outputs - } - - fn outputs_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.outputs - } - - fn default_outputs(&self) -> Vec { - vec!["shell".to_string()] - } - - fn default_options(&self) -> std::collections::HashMap { - std::collections::HashMap::new() - } - - fn options(&self) -> &std::collections::HashMap { - &self.options - } - - fn options_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.options - } -} - -#[derive(Debug, Clone, Default)] -pub struct ShellPidMetaPlugin { - meta_name: String, - is_finalized: bool, - outputs: std::collections::HashMap, - options: std::collections::HashMap, -} - -impl ShellPidMetaPlugin { - pub fn new( - _options: Option>, - outputs: Option>, - ) -> ShellPidMetaPlugin { - // Start with default options - let mut final_options = std::collections::HashMap::new(); - if let Some(opts) = _options { - for (key, value) in opts { - final_options.insert(key, value); - } - } - - // Start with default outputs - let mut final_outputs = std::collections::HashMap::new(); - let default_outputs = Self::default().default_outputs(); - for output_name in default_outputs { - final_outputs.insert(output_name.clone(), serde_yaml::Value::String(output_name)); - } - if let Some(outs) = outputs { - for (key, value) in outs { - final_outputs.insert(key, value); - } - } - - ShellPidMetaPlugin { - meta_name: "shell_pid".to_string(), - is_finalized: false, - outputs: final_outputs, - options: final_options, - } - } - - pub fn new_simple() -> ShellPidMetaPlugin { - Self::new(None, None) - } -} - -impl MetaPlugin for ShellPidMetaPlugin { - fn is_finalized(&self) -> bool { - self.is_finalized - } - - fn set_finalized(&mut self, finalized: bool) { - self.is_finalized = finalized; - } - - fn finalize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - // Mark as finalized - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - } - } - - fn update(&mut self, _data: &[u8]) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process more data - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: false, - } - } - - fn meta_name(&self) -> String { - self.meta_name.clone() - } - - fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - let mut metadata = Vec::new(); - let pid = match env::var("PPID") { - Ok(ppid) => ppid, - Err(_) => process::id().to_string(), - }; - - // Use process_metadata_outputs to handle output mapping - if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs( - "shell_pid", - pid, - &self.outputs - ) { - metadata.push(meta_data); - } - - // Mark as finalized since this plugin only needs to run once - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata, - is_finalized: true, - } - } - - fn outputs(&self) -> &std::collections::HashMap { - &self.outputs - } - - fn outputs_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.outputs - } - - fn default_outputs(&self) -> Vec { - vec!["shell_pid".to_string()] - } - - fn default_options(&self) -> std::collections::HashMap { - std::collections::HashMap::new() - } - - fn options(&self) -> &std::collections::HashMap { - &self.options - } - - fn options_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.options - } -} - -#[derive(Debug, Clone, Default)] -pub struct KeepPidMetaPlugin { - meta_name: String, - is_finalized: bool, - outputs: std::collections::HashMap, - options: std::collections::HashMap, -} - -impl KeepPidMetaPlugin { - pub fn new( - _options: Option>, - outputs: Option>, - ) -> KeepPidMetaPlugin { - // Start with default options - let mut final_options = std::collections::HashMap::new(); - if let Some(opts) = _options { - for (key, value) in opts { - final_options.insert(key, value); - } - } - - // Start with default outputs - let mut final_outputs = std::collections::HashMap::new(); - let default_outputs = Self::default().default_outputs(); - for output_name in default_outputs { - final_outputs.insert(output_name.clone(), serde_yaml::Value::String(output_name)); - } - if let Some(outs) = outputs { - for (key, value) in outs { - final_outputs.insert(key, value); - } - } - - KeepPidMetaPlugin { - meta_name: "keep_pid".to_string(), - is_finalized: false, - outputs: final_outputs, - options: final_options, - } - } - - pub fn new_simple() -> KeepPidMetaPlugin { - Self::new(None, None) - } -} - -impl MetaPlugin for KeepPidMetaPlugin { - fn is_finalized(&self) -> bool { - self.is_finalized - } - - fn set_finalized(&mut self, finalized: bool) { - self.is_finalized = finalized; - } - - fn finalize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - // Mark as finalized - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - } - } - - fn update(&mut self, _data: &[u8]) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process more data - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: false, - } - } - - fn meta_name(&self) -> String { - self.meta_name.clone() - } - - fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - let mut metadata = Vec::new(); - let pid = process::id().to_string(); - - // Use process_metadata_outputs to handle output mapping - if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs( - "keep_pid", - pid, - &self.outputs - ) { - metadata.push(meta_data); - } - - // Mark as finalized since this plugin only needs to run once - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata, - is_finalized: true, - } - } - - fn outputs(&self) -> &std::collections::HashMap { - &self.outputs - } - - fn outputs_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.outputs - } - - fn default_outputs(&self) -> Vec { - vec!["keep_pid".to_string()] - } - - fn default_options(&self) -> std::collections::HashMap { - std::collections::HashMap::new() - } - - fn options(&self) -> &std::collections::HashMap { - &self.options - } - - fn options_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.options - } -} - -#[derive(Debug, Clone, Default)] -pub struct HostnameMetaPlugin { - meta_name: String, - is_finalized: bool, - outputs: std::collections::HashMap, - options: std::collections::HashMap, -} - -impl HostnameMetaPlugin { - pub fn new( - options: Option>, - outputs: Option>, - ) -> HostnameMetaPlugin { - // Start with default options - let mut final_options = std::collections::HashMap::new(); - // Add default value for "full" option - final_options.insert("full".to_string(), serde_yaml::Value::Bool(true)); - if let Some(opts) = options { - for (key, value) in opts { - final_options.insert(key, value); - } - } - - // Start with default outputs - let mut final_outputs = std::collections::HashMap::new(); - let default_outputs = Self::default().default_outputs(); - for output_name in default_outputs { - final_outputs.insert(output_name.clone(), serde_yaml::Value::String(output_name)); - } - if let Some(outs) = outputs { - for (key, value) in outs { - final_outputs.insert(key, value); - } - } - - HostnameMetaPlugin { - meta_name: "hostname".to_string(), - is_finalized: false, - outputs: final_outputs, - options: final_options, - } - } - - pub fn new_simple() -> HostnameMetaPlugin { - Self::new(None, None) - } - - fn get_hostname(&self) -> String { - match gethostname().into_string() { - Ok(hostname) => hostname, - Err(_) => "unknown".to_string(), - } - } -} - -impl MetaPlugin for HostnameMetaPlugin { - fn is_finalized(&self) -> bool { - self.is_finalized - } - - fn set_finalized(&mut self, finalized: bool) { - self.is_finalized = finalized; - } - - fn finalize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - // Mark as finalized - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - } - } - - fn update(&mut self, _data: &[u8]) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process more data - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: false, - } - } - - fn meta_name(&self) -> String { - self.meta_name.clone() - } - - fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse { - // If already finalized, don't process again - if self.is_finalized { - return crate::meta_plugin::MetaPluginResponse { - metadata: Vec::new(), - is_finalized: true, - }; - } - - let mut metadata = Vec::new(); - - // Check if we should use full hostname or short hostname - let use_full = self.options.get("full") - .and_then(|v| v.as_bool()) - .unwrap_or(true); // Default to true - - let hostname = if use_full { - self.get_hostname() - } else { - // Get short hostname (first part before the first dot) - let full_hostname = self.get_hostname(); - full_hostname.split('.').next().unwrap_or(&full_hostname).to_string() - }; - - // Use process_metadata_outputs to handle output mapping - if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs( - "hostname", - hostname, - &self.outputs - ) { - metadata.push(meta_data); - } - - // Mark as finalized since this plugin only needs to run once - self.is_finalized = true; - - crate::meta_plugin::MetaPluginResponse { - metadata, - is_finalized: true, - } - } - - fn outputs(&self) -> &std::collections::HashMap { - &self.outputs - } - - fn outputs_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.outputs - } - - fn default_outputs(&self) -> Vec { - vec!["hostname".to_string()] - } - - fn default_options(&self) -> std::collections::HashMap { - let mut options = std::collections::HashMap::new(); - options.insert("full".to_string(), serde_yaml::Value::Bool(true)); - options - } - - fn options(&self) -> &std::collections::HashMap { - &self.options - } - - fn options_mut(&mut self) -> &mut std::collections::HashMap { - &mut self.options - } - - fn configure_options(&mut self, options: &std::collections::HashMap) -> anyhow::Result<()> { - for (key, value) in options { - self.options.insert(key.clone(), value.clone()); - } - Ok(()) - } -} -