diff --git a/src/meta_plugin.rs b/src/meta_plugin.rs index 0205201..e433a57 100644 --- a/src/meta_plugin.rs +++ b/src/meta_plugin.rs @@ -10,7 +10,7 @@ pub mod text; use crate::meta_plugin::program::MetaPluginProgram; use crate::meta_plugin::digest::{DigestSha256MetaPlugin, ReadTimeMetaPlugin, ReadRateMetaPlugin}; -use crate::meta_plugin::system::{CwdMetaPlugin, UserMetaPlugin, ShellMetaPlugin, ShellPidMetaPlugin, KeepPidMetaPlugin, HostnameMetaPlugin}; +use crate::meta_plugin::system::*; use crate::meta_plugin::magic::MagicFileMetaPlugin; use crate::meta_plugin::binary::BinaryMetaPlugin; use crate::meta_plugin::text::TextMetaPlugin; diff --git a/src/meta_plugin/system/cwd.rs b/src/meta_plugin/system/cwd.rs new file mode 100644 index 0000000..1d368c1 --- /dev/null +++ b/src/meta_plugin/system/cwd.rs @@ -0,0 +1,132 @@ +use std::env; +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() + } +} diff --git a/src/meta_plugin/system/hostname.rs b/src/meta_plugin/system/hostname.rs new file mode 100644 index 0000000..c9d51b7 --- /dev/null +++ b/src/meta_plugin/system/hostname.rs @@ -0,0 +1,179 @@ +use gethostname::gethostname; +use crate::meta_plugin::MetaPlugin; + +#[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(()) + } +} diff --git a/src/meta_plugin/system/keep_pid.rs b/src/meta_plugin/system/keep_pid.rs new file mode 100644 index 0000000..395a938 --- /dev/null +++ b/src/meta_plugin/system/keep_pid.rs @@ -0,0 +1,149 @@ +use std::process; +use crate::meta_plugin::MetaPlugin; + +#[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 + } +} diff --git a/src/meta_plugin/system/mod.rs b/src/meta_plugin/system/mod.rs new file mode 100644 index 0000000..f124c11 --- /dev/null +++ b/src/meta_plugin/system/mod.rs @@ -0,0 +1,13 @@ +pub mod cwd; +pub mod user; +pub mod shell; +pub mod shell_pid; +pub mod keep_pid; +pub mod hostname; + +pub use cwd::CwdMetaPlugin; +pub use user::UserMetaPlugin; +pub use shell::ShellMetaPlugin; +pub use shell_pid::ShellPidMetaPlugin; +pub use keep_pid::KeepPidMetaPlugin; +pub use hostname::HostnameMetaPlugin; diff --git a/src/meta_plugin/system/shell.rs b/src/meta_plugin/system/shell.rs new file mode 100644 index 0000000..67678dd --- /dev/null +++ b/src/meta_plugin/system/shell.rs @@ -0,0 +1,152 @@ +use std::env; +use crate::meta_plugin::MetaPlugin; + +#[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 + } +} diff --git a/src/meta_plugin/system/shell_pid.rs b/src/meta_plugin/system/shell_pid.rs new file mode 100644 index 0000000..f5f8bcf --- /dev/null +++ b/src/meta_plugin/system/shell_pid.rs @@ -0,0 +1,153 @@ +use std::env; +use std::process; +use crate::meta_plugin::MetaPlugin; + +#[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 + } +} diff --git a/src/meta_plugin/system/user.rs b/src/meta_plugin/system/user.rs new file mode 100644 index 0000000..4ef9d48 --- /dev/null +++ b/src/meta_plugin/system/user.rs @@ -0,0 +1,105 @@ +use std::env; +use crate::meta_plugin::MetaPlugin; + +#[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() + } +}