From 786af22672af1e487982dc04465f5fdb4c91f1cd Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 19 Sep 2025 12:41:21 +0200 Subject: [PATCH 01/44] Add procfs dependency --- Cargo.lock | 30 ++++++++++++++++++++++++++++-- sysminion/Cargo.toml | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c075af1b..ae34c98a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4011,7 +4011,7 @@ version = "0.2.0" dependencies = [ "libmodcore", "libsysinspect", - "procfs", + "procfs 0.17.0", "serde", "serde_json", "serde_yaml", @@ -4046,10 +4046,24 @@ dependencies = [ "chrono", "flate2", "hex", - "procfs-core", + "procfs-core 0.17.0", "rustix 0.38.44", ] +[[package]] +name = "procfs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" +dependencies = [ + "bitflags 2.9.4", + "chrono", + "flate2", + "procfs-core 0.18.0", + "rustix 1.1.2", + "serde", +] + [[package]] name = "procfs-core" version = "0.17.0" @@ -4061,6 +4075,17 @@ dependencies = [ "hex", ] +[[package]] +name = "procfs-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" +dependencies = [ + "bitflags 2.9.4", + "chrono", + "hex", +] + [[package]] name = "prost" version = "0.13.5" @@ -5948,6 +5973,7 @@ dependencies = [ "libsysinspect", "log", "once_cell", + "procfs 0.18.0", "rand 0.9.2", "regex", "reqwest", diff --git a/sysminion/Cargo.toml b/sysminion/Cargo.toml index 99c4eae7..a9ce732f 100644 --- a/sysminion/Cargo.toml +++ b/sysminion/Cargo.toml @@ -40,3 +40,4 @@ daemonize = "0.5.0" libc = "0.2.175" async-trait = "0.1.89" futures = "0.3.31" +procfs = { version = "0.18.0", features = ["serde"] } From 98a4e64ee1d1fd0db296a91e8d94b50c1dc21422 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 19 Sep 2025 12:41:40 +0200 Subject: [PATCH 02/44] Implement process/task counter per a minion --- sysminion/src/ptcounter.rs | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 sysminion/src/ptcounter.rs diff --git a/sysminion/src/ptcounter.rs b/sysminion/src/ptcounter.rs new file mode 100644 index 00000000..19dcf1b7 --- /dev/null +++ b/sysminion/src/ptcounter.rs @@ -0,0 +1,51 @@ +use procfs::Current; + +/// Process and task counter. +/// This is used to keep track of the number of processes and tasks +/// running on the minion, to avoid overloading it. + +#[derive(Debug, Clone)] +pub struct PTCounter { + tasks: usize, + loadaverage: f32, +} + +impl PTCounter { + /// Create new counter + pub fn new() -> Self { + Self { tasks: 0, loadaverage: 0.0 } + } + + /// Update load average from /proc/loadavg + /// This is called internally on each inc/dec operation. + /// More stats will/should be added here. + fn update_stats(&mut self) { + if let Ok(la) = procfs::LoadAverage::current() { + self.loadaverage = la.five; + } + } + + /// Increment task counter + pub fn inc(&mut self) { + self.update_stats(); + self.tasks += 1; + } + + /// Decrement task counter + pub fn dec(&mut self) { + if self.tasks > 0 { + self.tasks -= 1; + } + self.update_stats(); + } + + /// Get current task count + pub fn get_tasks(&self) -> usize { + self.tasks + } + + /// Get current load average (5 min) + pub fn get_loadaverage(&self) -> f32 { + self.loadaverage + } +} From e5900605e13dda6738ebab868c416d3a375a0af9 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 19 Sep 2025 12:42:19 +0200 Subject: [PATCH 03/44] Add task counting in the minion --- sysminion/src/main.rs | 14 ++++++++------ sysminion/src/minion.rs | 15 +++++++++++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/sysminion/src/main.rs b/sysminion/src/main.rs index d1968d67..577b0348 100644 --- a/sysminion/src/main.rs +++ b/sysminion/src/main.rs @@ -3,6 +3,7 @@ mod clidef; mod filedata; mod minion; mod proto; +mod ptcounter; mod rsa; use clap::{ArgMatches, Command}; @@ -61,13 +62,14 @@ fn get_config(params: &ArgMatches) -> MinionConfig { fn help(cli: &mut Command, params: ArgMatches) -> bool { for sc in ["setup", "module"] { if let Some(sub) = params.subcommand_matches(sc) - && sub.get_flag("help") { - if let Some(s_cli) = cli.find_subcommand_mut(sc) { - _ = s_cli.print_help(); - return true; - } - return false; + && sub.get_flag("help") + { + if let Some(s_cli) = cli.find_subcommand_mut(sc) { + _ = s_cli.print_help(); + return true; } + return false; + } } if params.get_flag("help") { diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index a3d41b02..1a548594 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -5,6 +5,7 @@ use crate::{ self, msg::{CONNECTION_TX, ExitState}, }, + ptcounter::PTCounter, rsa::MinionRSAKeyManager, }; use clap::ArgMatches; @@ -67,6 +68,8 @@ pub struct SysMinion { last_ping: Mutex, ping_timeout: Duration, + + pt_counter: Mutex, } impl SysMinion { @@ -85,6 +88,7 @@ impl SysMinion { filedata: Mutex::new(MinionFiledata::new(cfg.models_dir())?), last_ping: Mutex::new(Instant::now()), ping_timeout: Duration::from_secs(10), + pt_counter: Mutex::new(PTCounter::new()), }; log::debug!("Instance set up with root directory at {}", cfg.root_dir().to_str().unwrap_or_default()); instance.init()?; @@ -340,6 +344,7 @@ impl SysMinion { pub async fn send_fin_callback(self: Arc, ar: ActionResponse) -> Result<(), SysinspectError> { log::debug!("Sending fin sync callback on {}", ar.aid()); self.request(MinionMessage::new(self.get_minion_id(), RequestType::ModelEvent, json!(ar).to_string()).sendable()?).await; + self.as_ptr().pt_counter.lock().await.dec(); Ok(()) } @@ -473,6 +478,7 @@ impl SysMinion { match tokio::task::spawn_blocking(move || futures::executor::block_on(sr.start())).await { Ok(()) => { + self.as_ptr().pt_counter.lock().await.inc(); log::debug!("Sysinspect model cycle finished"); } Err(e) => { @@ -540,10 +546,11 @@ impl SysMinion { if !hostname.is_empty() { for hq in tgt.hostnames() { if let Ok(hq) = glob::Pattern::new(hq) - && hq.matches(&hostname) { - skip = false; - break; - } + && hq.matches(&hostname) + { + skip = false; + break; + } } if skip { log::trace!("Command was dropped as it is specifically targeting different hosts"); From 352c9d7cde8c1677434b9a8cd779d2bbbf93afa8 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 19 Sep 2025 12:42:33 +0200 Subject: [PATCH 04/44] Start documentation about virtual minions --- docs/genusage/overview.rst | 1 + docs/genusage/virtual_minions.rst | 89 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 docs/genusage/virtual_minions.rst diff --git a/docs/genusage/overview.rst b/docs/genusage/overview.rst index b7cc468c..513ff548 100644 --- a/docs/genusage/overview.rst +++ b/docs/genusage/overview.rst @@ -49,3 +49,4 @@ sections: distributed_model systraits targeting + virtual_minions diff --git a/docs/genusage/virtual_minions.rst b/docs/genusage/virtual_minions.rst new file mode 100644 index 00000000..6bb52218 --- /dev/null +++ b/docs/genusage/virtual_minions.rst @@ -0,0 +1,89 @@ +.. raw:: html + + + +.. role:: u + :class: underlined + +.. role:: bi + :class: bolditalic + +.. _global_configuration: + +Clusters and Virtual Minions +============================ + +Minions can be organized into groups called clusters. A cluster is a collection of minions that share a common +configuration and model description. This allows you to manage multiple minions as a single unit. + +A cluster acts like a "virtual minion." Instead of interacting with each minion individually, you can interact with +the cluster as a whole. For example, if you have several minions that are responsible for log analysis, you can create +a cluster named "log analyser" and assign those minions to it. The cluster will have all the necessary modules and +settings for log analysis. + +When you run a function on a cluster, the system can either use all minions in the cluster or select the one that is +least busy to perform the task. This makes it easier to manage workloads and ensures that tasks are distributed +efficiently among the available minions. +This configuration example demonstrates how to define "clustered minions" using a YAML structure. Each virtual minion is +described by a unique `id` and a `hostname`, which serve as labels for identification and grouping purposes. You can +assign custom `traits` to each virtual minion, allowing you to specify characteristics or metadata that can be used for +targeting or filtering. + +Virtual Minion Definition +-------------------------------- + +The `nodes` section under each virtual minion defines how physical minions are matched and associated with the virtual +minion. There are several ways to specify these matches: + + - By unique physical minion ID (e.g., `/etc/machine-id`), allowing precise targeting of individual machines. + + - By query patterns (such as domain names with wildcards), enabling selection of groups of minions based on naming + conventions. + + - By specifying required traits (e.g., operating system type, memory size), which allows for dynamic selection based on + system properties. + + - By combining queries and trait filters, you can create complex selection criteria, such as targeting all minions with a + certain name prefix and a minimum amount of memory. + +This flexible configuration enables you to create logical groupings of physical minions, assign them virtual identities, +and target them for orchestration, monitoring, or other management tasks based on a wide range of criteria. + +.. code-block:: yaml + + # Example configuration for clustered minions + + clustered-minions: + # Each minion has a virtual ID and virtual hostname + # These are basically just labels + - id: 12345 + hostname: fustercluck + # Virtual traits by which virtual minions are targeted + traits: + key: value + + # Physical minion matches + nodes: + # Matches a very specific minion by its /etc/machine-id + - id: 30490239492034995 + + # Matches all minions configured with domain name started with "web" prefix + - query: "web*" + + # Matches all minions those are OS linux + - traits: + system.os = "linux" + + # Matches all minions configured with domain name started with "web" prefix, + # but selects only those system memory is more than 8Gb RAM + - query: "web*" + traits: + system.mem > 8Gb From 6831d0042906d2b6b7cafc55fda675d6ab51eaea Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 19 Sep 2025 18:16:18 +0200 Subject: [PATCH 05/44] Expand documentation on virtual minions --- docs/genusage/virtual_minions.rst | 36 ++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/genusage/virtual_minions.rst b/docs/genusage/virtual_minions.rst index 6bb52218..67bf6671 100644 --- a/docs/genusage/virtual_minions.rst +++ b/docs/genusage/virtual_minions.rst @@ -57,6 +57,40 @@ minion. There are several ways to specify these matches: This flexible configuration enables you to create logical groupings of physical minions, assign them virtual identities, and target them for orchestration, monitoring, or other management tasks based on a wide range of criteria. +Configuration starts with the `clustered-minions` key, which contains a list of virtual minion definitions. Each virtual minion is defined +as a dictionary with the following keys: + + - `id`: A unique identifier for the virtual minion. Typically, this could be a UUID or any other unique string. + + - `hostname`: The hostname for the virtual minion. + + - `traits`: A dictionary of traits that can be used to target the virtual minion. Virtual minions can have only static traits, defined in this dictionary. + + - `nodes`: A list of physical minion matches. Each match can be defined in one of the following ways: + + `id` + + A specific physical minion ID (e.g., `/etc/machine-id`). + The `id` is dead-precise and matches the exact minion. In this case no more qualifiers are needed. + Just add all the minion IDs you want to be part of this virtual minion and that's it. + + `query` + + A query string that matches multiple physical minions (e.g., domain name patterns). + + `traits` + + A dictionary of traits that must be matched by the physical minion. + + `query` and `traits` + + Combining these two allows you to create more complex matching criteria. + +.. hint:: + + Keep it simple. While you **can** define complex matching criteria, it doesn't mean you **should** do that. + It's often best to start with straightforward configurations using just the `id` and then expand as needed in a future. + .. code-block:: yaml # Example configuration for clustered minions @@ -85,5 +119,5 @@ and target them for orchestration, monitoring, or other management tasks based o # Matches all minions configured with domain name started with "web" prefix, # but selects only those system memory is more than 8Gb RAM - query: "web*" - traits: + traits: system.mem > 8Gb From c179dd2376e1199ff48142c1a327130908c6ff11 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Tue, 16 Dec 2025 13:55:52 +0100 Subject: [PATCH 06/44] Update docs about virt minions --- docs/genusage/virtual_minions.rst | 103 +++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/docs/genusage/virtual_minions.rst b/docs/genusage/virtual_minions.rst index 67bf6671..3a868169 100644 --- a/docs/genusage/virtual_minions.rst +++ b/docs/genusage/virtual_minions.rst @@ -21,21 +21,69 @@ Clusters and Virtual Minions ============================ -Minions can be organized into groups called clusters. A cluster is a collection of minions that share a common -configuration and model description. This allows you to manage multiple minions as a single unit. - -A cluster acts like a "virtual minion." Instead of interacting with each minion individually, you can interact with -the cluster as a whole. For example, if you have several minions that are responsible for log analysis, you can create -a cluster named "log analyser" and assign those minions to it. The cluster will have all the necessary modules and -settings for log analysis. - -When you run a function on a cluster, the system can either use all minions in the cluster or select the one that is -least busy to perform the task. This makes it easier to manage workloads and ensures that tasks are distributed -efficiently among the available minions. -This configuration example demonstrates how to define "clustered minions" using a YAML structure. Each virtual minion is -described by a unique `id` and a `hostname`, which serve as labels for identification and grouping purposes. You can -assign custom `traits` to each virtual minion, allowing you to specify characteristics or metadata that can be used for -targeting or filtering. +Why? +---- + +Unlike traditional configuration management systems, SysInspect is mainly an event-driven task launcher. It doesn’t try +to provide thousands of modules; instead, it focuses on a small set of simple primitives that reliably run workloads +based on a defined model. + +Overview +-------- + +Clustered virtual minions are useful when you have several physical minions that should behave like one logical unit. +Instead of changing the configuration or state of the virtual minion itself, you use it as a single control point to +run functions on the underlying physical minions. Those functions are meant to act on external systems or services, +for example to run orchestration workflows, kick off monitoring or data-collection jobs, or trigger other automation +outside of the minions that back the cluster. + +.. important:: + + 🚨 + + Virtual clustered minions are not designed and not meant to manage or change *their own configuration or state*. + They are primarily used to perform actions and/or launchging workloads that are affecting other external systems. + + For example, running jobs, collecting metrics, orchestrating tasks on **other systems**, etc β€” depends on a module + capabilities that is launched on behalf of the virtual minion. + +Minions can be grouped into logical collections called clusters. A cluster is simply a set of minions that share a +similar role, configuration, and model description, so you can treat them as a single unit instead of dealing with +each one separately. + +From the outside, a cluster behaves like a single "virtual minion." Rather than talking to every physical minion on +its own, you talk to the cluster, and the cluster fans work out to the underlying machines. For example, if you have +several minions doing log analysis, you can group them into a cluster called "log-analyser" and assign those minions +to it. The cluster then exposes the modules, configuration, and model needed for log analysis in one place. + +When you run a function against a cluster, SysInspect can either execute it on all member minions or choose one of +them (for example, the least busy node) to handle the job. This helps balance workloads and reduces the need to +manually pick which minion should do what, while still giving you a single, stable target to call. + +The configuration example below shows how to define these "clustered minions" using a YAML structure. Each virtual +minion is described by a unique `id` and a `hostname`, which act as labels for grouping and identification. You can +also attach custom `traits` to each virtual minion, so you can target or filter them later based on those attributes. + +Caveats and Considerations +-------------------------- + +- A virtual minion is only as reliable as the real machines behind it. If some of them are offline or misbehaving, the + virtual minion will also act flaky, fail calls, or give you incomplete results. + +- There is some performance overhead. A virtual minion adds another layer that has to fan out to all physical minions + and possibly aggregate their responses. Before running anything, the master first checks every configured physical + minion. While it does that, nothing gets scheduled, and if several minions are down, the virtual minion will feel + slow or half-broken. + +- All physical minions in one virtual minion must have the same modules installed and configured. Think of it like a + shared Python virtualenv: if one minion is missing a module or has it misconfigured, you will get weird failures or + hard-to-explain differences in behavior when calling the same function via the virtual minion. + + .. note:: + + ⚠️ + + All minions that belong to a given virtual minion must have the same set of modules installed and configured. Virtual Minion Definition -------------------------------- @@ -57,7 +105,7 @@ minion. There are several ways to specify these matches: This flexible configuration enables you to create logical groupings of physical minions, assign them virtual identities, and target them for orchestration, monitoring, or other management tasks based on a wide range of criteria. -Configuration starts with the `clustered-minions` key, which contains a list of virtual minion definitions. Each virtual minion is defined +Configuration starts with the `cluster` key, which contains a list of virtual minion definitions. Each virtual minion is defined as a dictionary with the following keys: - `id`: A unique identifier for the virtual minion. Typically, this could be a UUID or any other unique string. @@ -95,29 +143,26 @@ as a dictionary with the following keys: # Example configuration for clustered minions - clustered-minions: + cluster: # Each minion has a virtual ID and virtual hostname # These are basically just labels - id: 12345 hostname: fustercluck # Virtual traits by which virtual minions are targeted traits: - key: value + key: value # Physical minion matches nodes: - # Matches a very specific minion by its /etc/machine-id + # Matches a very specific minion by its /etc/machine-id - id: 30490239492034995 - # Matches all minions configured with domain name started with "web" prefix - - query: "web*" + # Matches by the hostname + hostname: minion-01.example.com - # Matches all minions those are OS linux - - traits: - system.os = "linux" - - # Matches all minions configured with domain name started with "web" prefix, - # but selects only those system memory is more than 8Gb RAM - - query: "web*" + query: "minion-*.example.com" + # Matches all minions those are OS linux as well as system memory greater than 8Gb traits: - system.mem > 8Gb + system.os: "linux" + system.mem: "> 8Gb" + From e3dbda8b990b7e9ef106bf48af1890c9ac48eb35 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Tue, 16 Dec 2025 13:56:56 +0100 Subject: [PATCH 07/44] Extend configuration with the clustered minions --- libsysinspect/src/cfg/mmconf.rs | 131 ++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 15 deletions(-) diff --git a/libsysinspect/src/cfg/mmconf.rs b/libsysinspect/src/cfg/mmconf.rs index 2a6477c8..19567f0a 100644 --- a/libsysinspect/src/cfg/mmconf.rs +++ b/libsysinspect/src/cfg/mmconf.rs @@ -129,9 +129,10 @@ fn _logfile_path() -> PathBuf { for p in ["/var/log", &format!("{home}/.local/state"), "/tmp"] { let p = PathBuf::from(p); if let Ok(m) = fs::metadata(p.clone()) - && (m.permissions().mode() & 0o200) != 0 { - return p; - } + && (m.permissions().mode() & 0o200) != 0 + { + return p; + } } PathBuf::from("") } @@ -208,10 +209,11 @@ impl TelemetryConfig { let mut skipped = vec![]; for (key, value) in self.exporter_resources.clone().unwrap_or_default() { if let Some(v) = value.as_bool() - && !v { - skipped.push(key); - continue; - } + && !v + { + skipped.push(key); + continue; + } let value = util::dataconv::to_string(Some(value)).unwrap_or_default().trim().to_string(); if value.is_empty() { @@ -221,13 +223,15 @@ impl TelemetryConfig { resources.insert(key, value); } - for (k, v) in [("service.name", CFG_OTLP_SERVICE_NAME), + for (k, v) in [ + ("service.name", CFG_OTLP_SERVICE_NAME), ("service.namespace", CFG_OTLP_SERVICE_NAME), ("service.version", CFG_OTLP_SERVICE_VERSION), ("host.name", sysinfo::System::host_name().unwrap_or_default().as_str()), ("os.type", sysinfo::System::distribution_id().as_str()), ("deployment.environment", "production"), - ("os.version", sysinfo::System::kernel_version().unwrap_or_default().as_str())] { + ("os.version", sysinfo::System::kernel_version().unwrap_or_default().as_str()), + ] { if !resources.contains_key(k) && !skipped.contains(&k.to_string()) { resources.insert(k.to_string(), v.to_string()); } @@ -539,14 +543,102 @@ impl MinionConfig { return i; } if let Some((start, end)) = i.split_once('-') - && let (Ok(start), Ok(end)) = (start.parse::(), end.parse::()) { - return if end > start { rand::random::() % (end - start + 1) + start } else { start }; - } + && let (Ok(start), Ok(end)) = (start.parse::(), end.parse::()) + { + return if end > start { rand::random::() % (end - start + 1) + start } else { start }; + } rand::random::() % 30 + 5 } } +/// Definition of a clustered minion +/// It can match by: +/// - id: exact machine-id match +/// - hostname: wildcard domain name match +/// - traits: trait-based match +/// +/// At least one of the fields must be present. +/// If multiple fields are present, all must match. +/// +/// It contains a list of nodes, which are forming a group +/// of minions cluster as one logical minion entity. +/// At least two nodes must be present (it is a cluster after all). +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct ClusteredMinion { + id: Option, + hostname: Option, + traits: Option>, + nodes: Vec, +} + +impl ClusteredMinion { + /// Validate clustered minion definition + pub fn is_valid(&self) -> bool { + (self.id.is_some() || self.hostname.is_some() || self.traits.is_some()) && self.nodes.len() >= 2 + } + + /// Get clustered minion nodes + pub fn nodes(&self) -> &Vec { + &self.nodes + } + + /// Get clustered minion id + pub fn id(&self) -> Option<&Value> { + self.id.as_ref() + } + + /// Get clustered minion hostname + pub fn hostname(&self) -> Option<&String> { + self.hostname.as_ref() + } + + /// Get clustered minion traits + pub fn traits(&self) -> Option<&IndexMap> { + self.traits.as_ref() + } +} + +/// Definition of a clustered minion scope +/// +/// It can match by: +/// - id: exact machine-id match +/// - query: wildcard domain name match +/// - traits: trait-based match +/// +/// At least one of the fields must be present. +/// If multiple fields are present, all must match. +/// +/// The scope will return one or more minions matching the criteria. +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct ClusteredMinionScope { + id: Option, + query: Option, + traits: Option>, +} + +impl ClusteredMinionScope { + /// Validate clustered minion scope definition + pub fn is_valid(&self) -> bool { + self.id.is_some() || self.query.is_some() || self.traits.is_some() + } + + /// Get clustered minion id + pub fn id(&self) -> Option<&Value> { + self.id.as_ref() + } + + /// Get clustered minion scope query + pub fn query(&self) -> Option<&String> { + self.query.as_ref() + } + + /// Get clustered minion scope traits + pub fn traits(&self) -> Option<&IndexMap> { + self.traits.as_ref() + } +} + #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct MasterConfig { // Bind IP listener. Default "the world", i.e. 0.0.0.0 @@ -627,6 +719,9 @@ pub struct MasterConfig { // Configuration of history keeping in the database per each cycle call history: Option, + + // Clustered minions configuration + cluster: Option>, } impl MasterConfig { @@ -656,9 +751,10 @@ impl MasterConfig { pub fn otlp_compression(&self) -> String { let mut cpr = CFG_OTLP_COMPRESSION.to_string(); if let Some(cfg) = &self.telemetry - && let Some(compression) = &cfg.compression { - cpr = compression.clone(); - } + && let Some(compression) = &cfg.compression + { + cpr = compression.clone(); + } if !cpr.eq("gzip") && !cpr.eq("zstd") { return CFG_OTLP_COMPRESSION.to_string(); @@ -826,6 +922,11 @@ impl MasterConfig { pub fn telemetry_enabled(&self) -> bool { self.telemetry.is_some() && self.telemetry.as_ref().unwrap().collector.is_some() } + + /// Get clustered minions configuration + pub fn cluster(&self) -> Vec { + self.cluster.clone().unwrap_or_default() + } } #[derive(Debug, Serialize, Deserialize, Default, Clone)] From 3ec89fb0fcb65d5f834d0ed2db4b0fa0976ef2d7 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Tue, 16 Dec 2025 14:11:40 +0100 Subject: [PATCH 08/44] Send enhanced payloads as JSON instead of String --- libsysinspect/src/proto/mod.rs | 6 ++-- libsysinspect/src/proto/rqtypes.rs | 9 ++++++ libsysinspect/src/traits/systraits.rs | 12 ++++++-- sysmaster/src/master.rs | 28 +++++++++++++----- sysminion/src/minion.rs | 42 ++++++++++++++++++++------- sysminion/src/proto.rs | 17 +++++------ 6 files changed, 81 insertions(+), 33 deletions(-) diff --git a/libsysinspect/src/proto/mod.rs b/libsysinspect/src/proto/mod.rs index e8b14e08..ef48e45d 100644 --- a/libsysinspect/src/proto/mod.rs +++ b/libsysinspect/src/proto/mod.rs @@ -90,7 +90,7 @@ pub struct MinionMessage { request: RequestType, #[serde(rename = "d")] - data: String, + data: Value, #[serde(rename = "c")] retcode: usize, @@ -98,7 +98,7 @@ pub struct MinionMessage { impl MinionMessage { /// Message constructor - pub fn new(id: String, rtype: RequestType, data: String) -> MinionMessage { + pub fn new(id: String, rtype: RequestType, data: Value) -> MinionMessage { MinionMessage { id, request: rtype, data, retcode: ProtoErrorCode::Undef as usize, sid: "".to_string() } } @@ -141,7 +141,7 @@ impl MinionMessage { } /// Get payload - pub fn payload(&self) -> &str { + pub fn payload(&self) -> &Value { &self.data } } diff --git a/libsysinspect/src/proto/rqtypes.rs b/libsysinspect/src/proto/rqtypes.rs index 8ac08217..c79458be 100644 --- a/libsysinspect/src/proto/rqtypes.rs +++ b/libsysinspect/src/proto/rqtypes.rs @@ -1,5 +1,14 @@ use serde::{Deserialize, Serialize}; +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum ProtoValue { + #[serde(rename = "pt:g")] + PingTypeGeneral, + + #[serde(rename = "pt:d")] + PingTypeDiscovery, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub enum RequestType { /// Minion registration request or context. diff --git a/libsysinspect/src/traits/systraits.rs b/libsysinspect/src/traits/systraits.rs index c2fcb753..1b1d3da7 100644 --- a/libsysinspect/src/traits/systraits.rs +++ b/libsysinspect/src/traits/systraits.rs @@ -131,9 +131,10 @@ impl SystemTraits { // Machine Id (not always there) let mut mid = String::default(); if self.cfg.machine_id_path().exists() - && let Ok(id) = fs::read_to_string(self.cfg.machine_id_path()) { - mid = id.trim().to_string(); - } + && let Ok(id) = fs::read_to_string(self.cfg.machine_id_path()) + { + mid = id.trim().to_string(); + } self.put(SYS_ID.to_string(), json!(mid)); // Memory @@ -256,4 +257,9 @@ impl SystemTraits { pub fn to_json_string(&self) -> Result { Ok(serde_json::to_string(&json!(self.data))?) } + + /// Convert the data to the JSON value + pub fn to_json_value(&self) -> Result { + Ok(json!(self.data)) + } } diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 5842f1b4..3dadaab9 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -17,8 +17,11 @@ use libsysinspect::{ cfg::mmconf::{CFG_MODELS_ROOT, MasterConfig}, mdescr::{mspec::MODEL_FILE_EXT, mspecdef::ModelSpec, telemetry::DataExportType}, proto::{ - self, MasterMessage, MinionMessage, MinionTarget, ProtoConversion, errcodes::ProtoErrorCode, payload::ModStatePayload, query::SCHEME_COMMAND, - rqtypes::RequestType, + self, MasterMessage, MinionMessage, MinionTarget, ProtoConversion, + errcodes::ProtoErrorCode, + payload::ModStatePayload, + query::SCHEME_COMMAND, + rqtypes::{ProtoValue, RequestType}, }, util::{self, iofs::scan_files_sha256}, }; @@ -292,7 +295,7 @@ impl SysMaster { loop { if let Some((msg, minion_addr)) = rx.recv().await { let msg = String::from_utf8_lossy(&msg).to_string(); - log::trace!("Minion response: {minion_addr}: {msg}"); + log::debug!("Minion response: {minion_addr}: {msg}"); if let Some(req) = master.lock().await.to_request(&msg) { match req.req_type() { RequestType::Add => { @@ -304,7 +307,7 @@ impl SysMaster { let mut guard = c_master.lock().await; let resp_msg: &str; if !guard.mkr().is_registered(&c_mid) { - if let Err(err) = guard.mkr().add_mn_key(&c_mid, &minion_addr, req.payload()) { + if let Err(err) = guard.mkr().add_mn_key(&c_mid, &minion_addr, &req.payload().to_string()) { log::error!("Unable to add minion RSA key: {err}"); } guard.to_drop.insert(minion_addr.to_owned()); @@ -351,7 +354,15 @@ impl SysMaster { let c_id = req.id().to_string(); tokio::spawn(async move { let guard = c_master.lock().await; - guard.get_session().lock().await.ping(&c_id, None); + + let payload: HashMap = match serde_json::from_value(req.payload().clone()) { + Ok(data) => data, + Err(err) => { + log::error!("Unable to parse pong payload: {err}"); + return; + } + }; + guard.get_session().lock().await.ping(&c_id, payload.get("sid").map(|s| s.as_str())); let uptime = guard.get_session().lock().await.uptime(req.id()).unwrap_or_default(); log::trace!("Update last contacted for {} (alive for {:.2} min)", req.id(), uptime as f64 / 60.0); }); @@ -386,7 +397,8 @@ impl SysMaster { tokio::spawn(async move { let mut m = c_master.lock().await; let mrec = m.mreg.get(req.id()).unwrap_or_default().unwrap_or_default(); - let pl = match serde_json::from_str::>(req.payload()) { + // XXX: Fix this nonsense + let pl = match serde_json::from_str::>(req.payload().to_string().as_str()) { Ok(pl) => pl, Err(err) => { log::error!("An event message with the bogus payload: {err}"); @@ -440,7 +452,7 @@ impl SysMaster { let mut master = c_master.lock().await; let mrec = master.mreg.get(req.id()).unwrap_or_default().unwrap_or_default(); - let pl = match serde_json::from_str::>(req.payload()) { + let pl = match serde_json::from_str::>(req.payload().to_string().as_str()) { Ok(pl) => pl, Err(err) => { log::error!("An event message with the bogus payload: {err}"); @@ -601,7 +613,7 @@ impl SysMaster { tokio::spawn(async move { loop { _ = time::sleep(Duration::from_secs(5)).await; - let mut p = MasterMessage::new(RequestType::Ping, json!("")); + let mut p = MasterMessage::new(RequestType::Ping, json!(ProtoValue::PingTypeGeneral)); let mut t = MinionTarget::default(); t.add_hostname("*"); p.set_target(t); diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index 1a548594..b53b6496 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -31,7 +31,7 @@ use libsysinspect::{ errcodes::ProtoErrorCode, payload::{ModStatePayload, PayloadType}, query::{MinionQuery, SCHEME_COMMAND}, - rqtypes::RequestType, + rqtypes::{ProtoValue, RequestType}, }, reactor::fmt::{formatter::StringFormatter, kvfmt::KeyValueFormatter}, rsa, @@ -285,8 +285,23 @@ impl SysMinion { std::process::exit(1); } RequestType::Ping => { - self.request(proto::msg::get_pong()).await; - self.update_ping().await; + let p = msg.payload(); + + match serde_json::from_value::(p.clone()) { + Ok(ProtoValue::PingTypeGeneral) => { + log::debug!("Received general ping from master"); + self.request(proto::msg::get_pong(ProtoValue::PingTypeGeneral)).await; + self.update_ping().await; + } + Ok(ProtoValue::PingTypeDiscovery) => { + // XXX: On Discovery ping, we also send our traits and all the info for cluster + log::debug!("Received discovery ping from master"); + self.request(proto::msg::get_pong(ProtoValue::PingTypeDiscovery)).await; + } + Err(e) => { + log::warn!("Invalid ping payload `{}`: {}", p, e); + } + } } RequestType::ByeAck => { log::info!("Master confirmed shutdown, terminating"); @@ -302,7 +317,7 @@ impl SysMinion { } pub async fn send_traits(self: Arc) -> Result<(), SysinspectError> { - let mut r = MinionMessage::new(self.get_minion_id(), RequestType::Traits, traits::get_minion_traits(None).to_json_string()?); + let mut r = MinionMessage::new(self.get_minion_id(), RequestType::Traits, traits::get_minion_traits(None).to_json_value()?); r.set_sid(MINION_SID.to_string()); self.request(r.sendable().map_err(|e| { log::error!("Error preparing traits message: {e}"); @@ -314,8 +329,11 @@ impl SysMinion { /// Send ehlo pub async fn send_ehlo(self: Arc) -> Result<(), SysinspectError> { - let mut r = - MinionMessage::new(dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), RequestType::Ehlo, MINION_SID.to_string()); + let mut r = MinionMessage::new( + dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), + RequestType::Ehlo, + traits::get_minion_traits(None).to_json_value()?, + ); r.set_sid(MINION_SID.to_string()); log::info!("Ehlo on {}", self.cfg.master()); @@ -325,7 +343,7 @@ impl SysMinion { /// Send registration request pub async fn send_registration(self: Arc, pbk_pem: String) -> Result<(), SysinspectError> { - let r = MinionMessage::new(dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), RequestType::Add, pbk_pem); + let r = MinionMessage::new(dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), RequestType::Add, json!(pbk_pem)); log::info!("Registration request to {}", self.cfg.master()); self.request(r.sendable()?).await; @@ -336,21 +354,25 @@ impl SysMinion { pub async fn send_callback(self: Arc, ar: ActionResponse) -> Result<(), SysinspectError> { log::debug!("Sending sync callback on {}", ar.aid()); log::debug!("Callback: {ar:#?}"); - self.request(MinionMessage::new(self.get_minion_id(), RequestType::Event, json!(ar).to_string()).sendable()?).await; + self.request(MinionMessage::new(self.get_minion_id(), RequestType::Event, json!(ar)).sendable()?).await; Ok(()) } /// Send finalisation marker callback to the master on the results pub async fn send_fin_callback(self: Arc, ar: ActionResponse) -> Result<(), SysinspectError> { log::debug!("Sending fin sync callback on {}", ar.aid()); - self.request(MinionMessage::new(self.get_minion_id(), RequestType::ModelEvent, json!(ar).to_string()).sendable()?).await; + self.request(MinionMessage::new(self.get_minion_id(), RequestType::ModelEvent, json!(ar)).sendable()?).await; self.as_ptr().pt_counter.lock().await.dec(); Ok(()) } /// Send bye message pub async fn send_bye(self: Arc) { - let r = MinionMessage::new(dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), RequestType::Bye, MINION_SID.to_string()); + let r = MinionMessage::new( + dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), + RequestType::Bye, + json!(MINION_SID.to_string()), + ); log::info!("Goodbye to {}", self.cfg.master()); match r.sendable() { diff --git a/sysminion/src/proto.rs b/sysminion/src/proto.rs index 4ef65c5c..d87e5200 100644 --- a/sysminion/src/proto.rs +++ b/sysminion/src/proto.rs @@ -1,10 +1,10 @@ pub mod msg { - use std::sync::atomic::AtomicBool; + use std::{collections::HashMap, sync::atomic::AtomicBool}; use crate::minion::MINION_SID; use libsysinspect::{ SysinspectError, - proto::{MasterMessage, ProtoConversion}, + proto::{MasterMessage, ProtoConversion, rqtypes::ProtoValue}, }; use libsysinspect::{ proto::{MinionMessage, rqtypes::RequestType}, @@ -12,6 +12,7 @@ pub mod msg { util::dataconv, }; use once_cell::sync::Lazy; + use serde_json::{Value, json, to_value}; use tokio::sync::broadcast; /// Channel for master connection status @@ -31,13 +32,11 @@ pub mod msg { } /// Make pong message - pub fn get_pong() -> Vec { - let p = MinionMessage::new( - dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), - RequestType::Pong, - MINION_SID.to_string(), - ); - + pub fn get_pong(t: ProtoValue) -> Vec { + let mut data: HashMap = HashMap::new(); + data.insert("pt".to_string(), to_value(t).unwrap()); + data.insert("sid".to_string(), to_value(MINION_SID.to_string()).unwrap()); + let p = MinionMessage::new(dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), RequestType::Pong, json!(data)); if let Ok(data) = p.sendable() { return data; } From 3bf4c7ec974459bda392e59d5717bdc8186a6070 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 14:52:22 +0100 Subject: [PATCH 09/44] Add globset dependency --- Cargo.lock | 5 +++-- sysmaster/Cargo.toml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae34c98a..d1b6ad50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1773,9 +1773,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", @@ -5932,6 +5932,7 @@ dependencies = [ "daemonize", "ed25519-dalek", "futures", + "globset", "indexmap 2.11.1", "libc", "libeventreg", diff --git a/sysmaster/Cargo.toml b/sysmaster/Cargo.toml index 41a33c71..be0edfe3 100644 --- a/sysmaster/Cargo.toml +++ b/sysmaster/Cargo.toml @@ -43,3 +43,4 @@ daemonize = "0.5.0" indexmap = { version = "2.11.1", features = ["serde"] } chrono = { version = "0.4.42", features = ["serde"] } async-trait = "0.1.89" +globset = "0.4.18" From ebe6662410dfa2761da4463907bca0d35a1e235f Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 14:52:42 +0100 Subject: [PATCH 10/44] Update documentation on Virtual Minions: launching/invocation --- docs/genusage/virtual_minions.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/genusage/virtual_minions.rst b/docs/genusage/virtual_minions.rst index 3a868169..2731b24c 100644 --- a/docs/genusage/virtual_minions.rst +++ b/docs/genusage/virtual_minions.rst @@ -85,6 +85,29 @@ Caveats and Considerations All minions that belong to a given virtual minion must have the same set of modules installed and configured. + +Invocation +---------- + +Virtual minions are invoked with a different query syntax than regular minions. When you call all minions with +a `*` glob (or any kind of globbing), virtual minions are skipped. To call a virtual minion, you need to use +a `v:` prefix in the query, followed by the virtual minion hostname or glob pattern. For example: + +.. code-block:: bash + + sysinspect your/model 'v:*' + +Traits, however, remain the same, because `v:*` simply expands to all actual minions that back the virtual minion, +where traits query will filter them further. For example, if your cluster has four minions, but two of them are +Ubuntu Linux and the other are FreeBSD, you can call only the Linux ones like this: + +.. code-block:: bash + + sysinspect your/model 'v:*' -t 'system.os.name:Ubuntu' + +In this case, the virtual minion expands to all physical minions, but the trait filter narrows it down to just +the Ubuntu ones. + Virtual Minion Definition -------------------------------- From 8e56967c1fe5832bafa00e2ad40bc6728448937c Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 14:54:52 +0100 Subject: [PATCH 11/44] Lintfix imports --- sysminion/src/minion.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index b53b6496..b96d5adf 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -30,7 +30,10 @@ use libsysinspect::{ MasterMessage, MinionMessage, ProtoConversion, errcodes::ProtoErrorCode, payload::{ModStatePayload, PayloadType}, - query::{MinionQuery, SCHEME_COMMAND}, + query::{ + MinionQuery, SCHEME_COMMAND, + commands::{CLUSTER_REBOOT, CLUSTER_REMOVE_MINION, CLUSTER_ROTATE, CLUSTER_SHUTDOWN, CLUSTER_SYNC}, + }, rqtypes::{ProtoValue, RequestType}, }, reactor::fmt::{formatter::StringFormatter, kvfmt::KeyValueFormatter}, @@ -514,21 +517,21 @@ impl SysMinion { async fn call_internal_command(self: Arc, cmd: &str) { let cmd = cmd.strip_prefix(SCHEME_COMMAND).unwrap_or_default(); match cmd { - libsysinspect::proto::query::commands::CLUSTER_SHUTDOWN => { + CLUSTER_SHUTDOWN => { log::info!("Requesting minion shutdown from a master"); self.as_ptr().send_bye().await; } - libsysinspect::proto::query::commands::CLUSTER_REBOOT => { + CLUSTER_REBOOT => { log::warn!("Command \"reboot\" is not implemented yet"); } - libsysinspect::proto::query::commands::CLUSTER_ROTATE => { + CLUSTER_ROTATE => { log::warn!("Command \"rotate\" is not implemented yet"); } - libsysinspect::proto::query::commands::CLUSTER_REMOVE_MINION => { + CLUSTER_REMOVE_MINION => { log::info!("{} from the master", "Unregistering".bright_red().bold()); self.as_ptr().send_bye().await; } - libsysinspect::proto::query::commands::CLUSTER_SYNC => { + CLUSTER_SYNC => { log::info!("Syncing the minion with the master"); if let Err(e) = libmodpak::SysInspectModPakMinion::new(self.cfg.clone()).sync().await { log::error!("Failed to sync minion with master: {e}"); @@ -567,7 +570,7 @@ impl SysMinion { let hostname = dataconv::as_str(traits.get("system.hostname")); if !hostname.is_empty() { for hq in tgt.hostnames() { - if let Ok(hq) = glob::Pattern::new(hq) + if let Ok(hq) = glob::Pattern::new(&hq) && hq.matches(&hostname) { skip = false; From 23c19a0829ae27760ebb0e7f34c0b1985224bb97 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 14:55:42 +0100 Subject: [PATCH 12/44] Bugfix: remove hostname duplicates in query --- libsysinspect/src/proto/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libsysinspect/src/proto/mod.rs b/libsysinspect/src/proto/mod.rs index ef48e45d..a8389e8d 100644 --- a/libsysinspect/src/proto/mod.rs +++ b/libsysinspect/src/proto/mod.rs @@ -3,6 +3,8 @@ pub mod payload; pub mod query; pub mod rqtypes; +use std::collections::HashSet; + use crate::SysinspectError; use errcodes::ProtoErrorCode; use rqtypes::RequestType; @@ -164,7 +166,7 @@ pub struct MinionTarget { traits_query: String, #[serde(rename = "h")] - hostnames: Vec, + hostnames: HashSet, #[serde(rename = "cq")] context_query: String, @@ -177,7 +179,7 @@ impl MinionTarget { /// Add hostnames pub fn add_hostname(&mut self, hostname: &str) { - self.hostnames.push(hostname.to_string()); + self.hostnames.insert(hostname.to_string()); } pub fn id(&self) -> &String { @@ -188,8 +190,8 @@ impl MinionTarget { &self.sid } - pub fn hostnames(&self) -> &Vec { - &self.hostnames + pub fn hostnames(&self) -> Vec { + self.hostnames.iter().map(|s| s.to_string()).collect() } /// Get scheme From 22b995ec8aa5db0cd16f24b1251402141016f629 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 14:58:14 +0100 Subject: [PATCH 13/44] Rename items() to trait_keys() in trait lister --- libsysinspect/src/mdescr/mspec.rs | 2 +- libsysinspect/src/pylang/pylib/pysystem.rs | 2 +- libsysinspect/src/traits/systraits.rs | 33 ++++++++++++++++------ sysminion/src/minion.rs | 2 +- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/libsysinspect/src/mdescr/mspec.rs b/libsysinspect/src/mdescr/mspec.rs index 0a9ea7cf..a606d07e 100644 --- a/libsysinspect/src/mdescr/mspec.rs +++ b/libsysinspect/src/mdescr/mspec.rs @@ -41,7 +41,7 @@ impl SpecLoader { let mut ext: Option> = None; if let Some(traits) = traits { let mut et: IndexMap = IndexMap::new(); - for k in traits.items() { + for k in traits.trait_keys() { if let Some(v) = traits.get(&k) { et.insert(k, v); } diff --git a/libsysinspect/src/pylang/pylib/pysystem.rs b/libsysinspect/src/pylang/pylib/pysystem.rs index 90d3e6c3..ad3192be 100644 --- a/libsysinspect/src/pylang/pylib/pysystem.rs +++ b/libsysinspect/src/pylang/pylib/pysystem.rs @@ -94,7 +94,7 @@ pub mod syscore { fn list(&self, _vm: &VirtualMachine) -> StrVec { let mut out: StrVec = StrVec(vec![]); if let Some(traits) = self.traits.clone() { - for item in traits.items() { + for item in traits.trait_keys() { out.0.push(item); } } diff --git a/libsysinspect/src/traits/systraits.rs b/libsysinspect/src/traits/systraits.rs index 1b1d3da7..bcdf2247 100644 --- a/libsysinspect/src/traits/systraits.rs +++ b/libsysinspect/src/traits/systraits.rs @@ -22,12 +22,16 @@ pub struct SystemTraits { data: IndexMap, cfg: MinionConfig, checksum: String, + quiet: bool, } impl SystemTraits { - pub fn new(cfg: MinionConfig) -> SystemTraits { - log::debug!("Initialising system traits"); - let mut traits = SystemTraits { cfg, ..Default::default() }; + pub fn new(cfg: MinionConfig, quiet: bool) -> SystemTraits { + if !quiet { + log::debug!("Initialising system traits"); + } + + let mut traits = SystemTraits { cfg, quiet, ..Default::default() }; traits.get_system(); traits.get_network(); if let Err(err) = traits.get_defined() { @@ -66,7 +70,7 @@ impl SystemTraits { } /// Return known trait items - pub fn items(&self) -> Vec { + pub fn trait_keys(&self) -> Vec { let mut items = self.data.keys().map(|s| s.to_string()).collect::>(); items.sort(); @@ -101,7 +105,10 @@ impl SystemTraits { /// Read standard system traits fn get_system(&mut self) { - log::info!("Loading system traits data"); + if !self.quiet { + log::info!("Loading system traits data"); + } + let system = sysinfo::System::new_all(); // Common @@ -151,7 +158,9 @@ impl SystemTraits { /// Load network data fn get_network(&mut self) { - log::info!("Loading network traits data"); + if !self.quiet { + log::info!("Loading network traits data"); + } let net = sysinfo::Networks::new_with_refreshed_list(); for (ifs, data) in net.iter() { self.put(format!("system.net.{ifs}.mac"), json!(data.mac_address().to_string())); @@ -164,7 +173,9 @@ impl SystemTraits { /// Read defined/configured static traits fn get_defined(&mut self) -> Result<(), SysinspectError> { - log::debug!("Loading custom static traits data"); + if !self.quiet { + log::info!("Loading defined/custom traits"); + } for f in fs::read_dir(self.cfg.traits_dir())?.flatten() { let fname = f.file_name(); @@ -205,12 +216,16 @@ impl SystemTraits { /// Load custom functions fn get_functions(&mut self) -> Result<(), SysinspectError> { - log::info!("Loading trait functions"); + if !self.quiet { + log::info!("Loading trait functions"); + } for f in fs::read_dir(self.cfg.functions_dir())?.flatten() { let fname = f.path(); let fname = fname.file_name().unwrap_or_default().to_str().unwrap_or_default(); - log::info!("Calling function {fname}"); + if !self.quiet { + log::info!("Calling function {fname}"); + } let is_exec = match fs::metadata(f.path()) { Ok(m) => { diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index b96d5adf..a6991ebe 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -129,7 +129,7 @@ impl SysMinion { } let mut out: Vec = vec![]; - for t in traits::get_minion_traits(Some(&self.cfg)).items() { + for t in traits::get_minion_traits(Some(&self.cfg)).trait_keys() { out.push(format!("{}: {}", t.to_owned(), dataconv::to_string(traits::get_minion_traits(None).get(&t)).unwrap_or_default())); } log::debug!("Minion traits:\n{}", out.join("\n")); From 8d1d7849ce7a8bb0adae49441818ced91cec76d7 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 14:58:42 +0100 Subject: [PATCH 14/44] Fix keyval formatter: title was always off. Also add the ability to change the title, if needed --- libsysinspect/src/reactor/fmt/kvfmt.rs | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/libsysinspect/src/reactor/fmt/kvfmt.rs b/libsysinspect/src/reactor/fmt/kvfmt.rs index 7ed7e31e..3fb5abd0 100644 --- a/libsysinspect/src/reactor/fmt/kvfmt.rs +++ b/libsysinspect/src/reactor/fmt/kvfmt.rs @@ -1,14 +1,16 @@ use super::formatter::StringFormatter; use colored::Colorize; use prettytable::{ - format::{self}, Cell, Row, Table, + format::{self}, }; use serde_json::Value; use unicode_segmentation::UnicodeSegmentation; pub struct KeyValueFormatter { data: Value, + keytitle: String, + valtitle: String, } impl KeyValueFormatter { @@ -31,51 +33,49 @@ impl KeyValueFormatter { Value::Array(arr) => { tbl.add_row(Row::new(vec![Cell::new(&format!("{space}{key}")), Cell::new("")])); for elem in arr.iter() { - //self.to_table(table, &format!("{}", i + 1), elem, indent + 1); self.to_table(tbl, "", elem, offset + 1); } } Value::String(s) => { let cval = s.bright_green().to_string(); - tbl.add_row(Row::new(vec![ - Cell::new(&format!("{space}{key}")), - Cell::new(&format!("{: { let cval = n.to_string().bright_cyan(); - tbl.add_row(Row::new(vec![ - Cell::new(&format!("{space}{key}")), - Cell::new(&format!("{: { let cval = b.to_string().bright_red(); - tbl.add_row(Row::new(vec![ - Cell::new(&format!("{space}{key}")), - Cell::new(&format!("{: { - let cval = "null".yellow(); - tbl.add_row(Row::new(vec![ - Cell::new(&format!("{space}{key}")), - Cell::new(&format!("{: String { let mut table = Table::new(); // Add headers - table.add_row(Row::new(vec![Cell::new("Key"), Cell::new("Value")])); + //table.add_row(Row::new(vec![Cell::new(&self.keytitle), Cell::new(&self.valtitle)])); + table.add_row(Row::new(vec![Cell::new(&self.keytitle), Cell::new(&format!("{: Date: Wed, 17 Dec 2025 14:59:49 +0100 Subject: [PATCH 15/44] Enable cluster of virtual minions, add an ability to read the minion registered database --- sysmaster/src/cluster.rs | 129 +++++++++++++++++++++++++++++++++ sysmaster/src/main.rs | 1 + sysmaster/src/master.rs | 41 ++++++++--- sysmaster/src/registry/mreg.rs | 45 ++++++++++-- 4 files changed, 199 insertions(+), 17 deletions(-) create mode 100644 sysmaster/src/cluster.rs diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs new file mode 100644 index 00000000..1b0ec027 --- /dev/null +++ b/sysmaster/src/cluster.rs @@ -0,0 +1,129 @@ +// Cluster management for sysmaster + +use globset::Glob; +use libsysinspect::{SysinspectError, cfg::mmconf::ClusteredMinion, proto::MasterMessage}; +use serde_yaml::Value; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; +use tokio::sync::Mutex; + +use crate::registry::mreg::MinionRegistry; + +#[derive(Debug, Clone, Default)] +/// Representation of a clustered node +pub struct ClusterNode { + hostname: String, + mid: String, + traits: HashMap, +} + +/// Cluster node representation +impl ClusterNode { + /// Create a new cluster node + pub fn new(hostname: &str, mid: &str, traits: HashMap) -> ClusterNode { + ClusterNode { hostname: hostname.to_string(), mid: mid.to_string(), traits } + } + + /// Match hostname with glob pattern + fn match_hostname(&self, pattern: &str) -> bool { + match Glob::new(pattern) { + Ok(g) => g.compile_matcher().is_match(&self.hostname), + Err(_) => false, + } + } + + /// Match traits + fn matches_traits(&self, traits: &HashMap) -> bool { + for (k, v) in traits.iter() { + if let Some(tv) = self.traits.get(k) { + if tv != v { + return false; + } + } else { + return false; + } + } + true + } +} + +#[derive(Debug, Clone, Default)] +pub struct VirtualMinion { + id: String, + hostnames: Vec, + traits: HashMap, + minions: Vec, // Configured physical minions +} + +#[derive(Debug, Clone)] +pub struct VirtualMinionsCluster { + mreg: Arc>, + virtual_minions: Vec, // Configured clustered minions + /* + Here must be a sort of state of the cluster, e.g., which minion is + online, which is offline, last heartbeat time, their current load etc. + */ +} + +impl VirtualMinionsCluster { + pub fn new(cfg: Vec, mreg: Arc>) -> VirtualMinionsCluster { + if cfg.is_empty() { + return VirtualMinionsCluster { virtual_minions: Vec::new(), mreg }; + } + let mut v_minions: Vec = Vec::new(); + + // Call all that stuff in self.init() later to keep mreg async + for m in cfg.iter() { + let nodes: Vec = Vec::new(); + let id: String = m.id().and_then(|v| v.as_str()).unwrap_or_default().to_string(); + let traits: HashMap = match m.traits() { + Some(t) => t.clone().into_iter().collect(), + None => HashMap::new(), + }; + + for node in m.nodes().iter() { + let _node_traits: HashMap = match node.traits() { + Some(t) => t.clone().into_iter().collect(), + None => HashMap::new(), + }; + // TODO: find here minion nodes first + // First, glob the database, get minions matching the hostname pattern and/or traits. + // Then, create ClusterNode instances for each matched minion. + + //let node = ClusterNode::new(&node.hostname().cloned().unwrap_or_default(), &node.id().cloned().unwrap_or_default(), node_traits); + //nodes.push(node); + } + + let v_minion = VirtualMinion { id, hostnames: vec![m.hostname().cloned().unwrap_or_default()], traits, minions: nodes }; + v_minions.push(v_minion); + } + + VirtualMinionsCluster { virtual_minions: v_minions, mreg } + } + + pub async fn init(&self) -> Result<(), SysinspectError> { + let minions = self.mreg.lock().await.get_by_hostname_or_ip("*")?; + for mr in minions.iter() { + log::info!("Discovered minion in cluster init: {:?}", mr.id()); + } + Ok(()) + } + + /// Select a clustered minion by its hostname + /// Returns a list of precise minion IDs matching the criteria. + pub fn select(&self, _hostnames: Vec, _traits: Option>) -> Vec { + let ids: HashSet = HashSet::new(); + + ids.into_iter().collect() + } + + /// Create MasterMessages to be sent to selected minions + pub fn to_master_messages(&self, _query: &str) -> Vec { + Vec::new() + } + + /// Update cluster state by pong response from a minion + pub fn update_state(&mut self) {} +} diff --git a/sysmaster/src/main.rs b/sysmaster/src/main.rs index 4dbfc3f8..05ab8e39 100644 --- a/sysmaster/src/main.rs +++ b/sysmaster/src/main.rs @@ -1,4 +1,5 @@ mod clidef; +mod cluster; mod dataserv; mod master; mod master_itf; diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 3dadaab9..8cc6cd40 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -1,4 +1,5 @@ use crate::{ + cluster::VirtualMinionsCluster, dataserv::fls, registry::{ mkb::MinionsKeyRegistry, @@ -52,21 +53,34 @@ pub struct SysMaster { cfg: MasterConfig, broadcast: broadcast::Sender>, mkr: MinionsKeyRegistry, - mreg: MinionRegistry, + mreg: Arc>, evtipc: Arc, to_drop: HashSet, session: Arc>, ptr: Option>>, + vmcluster: VirtualMinionsCluster, } impl SysMaster { pub fn new(cfg: MasterConfig) -> Result { let (tx, _) = broadcast::channel::>(100); let mkr = MinionsKeyRegistry::new(cfg.minion_keys_root())?; - let mreg = MinionRegistry::new(cfg.minion_registry_root())?; + let mreg = Arc::new(Mutex::new(MinionRegistry::new(cfg.minion_registry_root())?)); + let evtreg = Arc::new(Mutex::new(EventsRegistry::new(cfg.telemetry_location(), cfg.history())?)); let evtipc = Arc::new(DbIPCService::new(Arc::clone(&evtreg), cfg.telemetry_socket().to_str().unwrap_or_default())?); - Ok(SysMaster { cfg, broadcast: tx, mkr, to_drop: HashSet::default(), session: Arc::clone(&SHARED_SESSION), mreg, evtipc, ptr: None }) + let vmc = VirtualMinionsCluster::new(cfg.cluster().to_owned(), Arc::clone(&mreg)); + Ok(SysMaster { + cfg, + broadcast: tx, + mkr, + to_drop: HashSet::default(), + session: Arc::clone(&SHARED_SESSION), + mreg, + evtipc, + ptr: None, + vmcluster: vmc, + }) } /// Open FIFO socket for command-line communication @@ -98,6 +112,7 @@ impl SysMaster { pub async fn init(&mut self) -> Result<(), SysinspectError> { log::info!("Starting master at {}", self.cfg.bind_addr()); self.open_socket(&self.cfg.socket())?; + self.vmcluster.init().await?; Ok(()) } @@ -159,7 +174,7 @@ impl SysMaster { if let Ok(s) = self.evtipc.get_last_session().await { for m in self.evtipc.get_minions(s.sid()).await.unwrap_or_default() { - let mrec = match self.mreg.get(m.id()) { + let mrec = match self.mreg.lock().await.get(m.id()) { Ok(Some(mrec)) => mrec, Ok(None) => { log::error!("Unable to get minion record"); @@ -184,7 +199,7 @@ impl SysMaster { if self.cfg.telemetry_enabled() { // Emit reduced data for (mid, res) in reducer.get_reduced_data() { - if let Ok(Some(mrec)) = self.mreg.get(mid) { + if let Ok(Some(mrec)) = self.mreg.lock().await.get(mid) { let fqdn = mrec.get_traits().get("system.hostname.fqdn").unwrap_or(&serde_json::Value::String("".to_string())).to_string(); libtelemetry::otel_log_json(res, vec![("hostname".into(), fqdn.into())]); } else { @@ -231,6 +246,8 @@ impl SysMaster { msg.set_target(tgt); msg.set_retcode(ProtoErrorCode::Success); + log::warn!("Querying minion(s) with: {msg:#?}"); + return Some(msg); } @@ -395,8 +412,8 @@ impl SysMaster { log::debug!("Event for {}: {}", req.id(), req.payload()); let c_master = Arc::clone(&master); tokio::spawn(async move { - let mut m = c_master.lock().await; - let mrec = m.mreg.get(req.id()).unwrap_or_default().unwrap_or_default(); + let m = c_master.lock().await; + let mrec = m.mreg.lock().await.get(req.id()).unwrap_or_default().unwrap_or_default(); // XXX: Fix this nonsense let pl = match serde_json::from_str::>(req.payload().to_string().as_str()) { Ok(pl) => pl, @@ -449,8 +466,8 @@ impl SysMaster { RequestType::ModelEvent => { let c_master = Arc::clone(&master); tokio::spawn(async move { - let mut master = c_master.lock().await; - let mrec = master.mreg.get(req.id()).unwrap_or_default().unwrap_or_default(); + let master = c_master.lock().await; + let mrec = master.mreg.lock().await.get(req.id()).unwrap_or_default().unwrap_or_default(); let pl = match serde_json::from_str::>(req.payload().to_string().as_str()) { Ok(pl) => pl, @@ -471,7 +488,7 @@ impl SysMaster { let mut otel = OtelLogger::new(&pl); otel.set_map(true); // Use mapper (only) match master.evtipc.get_events(sid.sid(), req.id()).await { - Ok(events) => match master.mreg.get(req.id()) { + Ok(events) => match master.mreg.lock().await.get(req.id()) { Ok(Some(mrec)) => { otel.feed(events, mrec); } @@ -506,7 +523,7 @@ impl SysMaster { pub async fn on_traits(&mut self, mid: String, payload: String) { let traits = serde_json::from_str::>(&payload).unwrap_or_default(); if !traits.is_empty() { - if let Err(err) = self.mreg.refresh(&mid, traits) { + if let Err(err) = self.mreg.lock().await.refresh(&mid, traits) { log::error!("Unable to sync traits: {err}"); } else { log::info!("Traits added"); @@ -517,7 +534,7 @@ impl SysMaster { pub async fn on_fifo_commands(&mut self, msg: &MasterMessage) { if msg.target().scheme().eq("cmd://cluster/minion/remove") && !msg.target().id().is_empty() { log::info!("Removing minion {}", msg.target().id()); - if let Err(err) = self.mreg.remove(msg.target().id()) { + if let Err(err) = self.mreg.lock().await.remove(msg.target().id()) { log::error!("Unable to remove minion {}: {err}", msg.target().id()); } if let Err(err) = self.mkr().remove_mn_key(msg.target().id()) { diff --git a/sysmaster/src/registry/mreg.rs b/sysmaster/src/registry/mreg.rs index 6276a7bc..8ae95e93 100644 --- a/sysmaster/src/registry/mreg.rs +++ b/sysmaster/src/registry/mreg.rs @@ -1,4 +1,5 @@ use super::rec::MinionRecord; +use globset::Glob; use libsysinspect::SysinspectError; use serde_json::{Value, json}; use sled::{Db, Tree}; @@ -6,7 +7,7 @@ use std::{collections::HashMap, fs, path::PathBuf}; const DB_MINIONS: &str = "minions"; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MinionRegistry { conn: Db, } @@ -92,14 +93,48 @@ impl MinionRegistry { Err(err) => return Err(SysinspectError::MasterGeneralError(format!("{err}"))), }; - if contains - && let Err(err) = minions.remove(mid) { - return Err(SysinspectError::MasterGeneralError(format!("{err}"))); - }; + if contains && let Err(err) = minions.remove(mid) { + return Err(SysinspectError::MasterGeneralError(format!("{err}"))); + }; Ok(()) } + /// Get minion records by hostname or IP address (fall-back) + /// Receives hostname or IP address or any of these as glob pattern and returns matching minion records. + pub fn get_by_hostname_or_ip(&mut self, hostname: &str) -> Result, SysinspectError> { + let host_matcher = match Glob::new(hostname) { + Ok(g) => g, + Err(err) => return Err(SysinspectError::MasterGeneralError(format!("Unable to compile hostname glob pattern: {err}"))), + }; + + let minions = self.get_tree(DB_MINIONS)?; + let mut records: Vec = Vec::default(); + for entry in minions.iter() { + match entry { + Ok((_k_ent, v_ent)) => { + let mrec = serde_json::from_str::(&String::from_utf8(v_ent.to_vec()).unwrap_or_default()); + let mrec = match mrec { + Ok(mrec) => mrec, + Err(err) => return Err(SysinspectError::MasterGeneralError(format!("Unable to read minion record: {err}"))), + }; + + for tr in ["system.hostname", "system.hostname.fqdn", "system.hostname.ip"] { + if let Some(tr_val) = mrec.get_traits().get(tr) + && let Some(tr_val) = tr_val.as_str() + && host_matcher.compile_matcher().is_match(tr_val) + { + records.push(mrec.clone()); + break; + } + } + } + Err(err) => return Err(SysinspectError::MasterGeneralError(format!("Minion database seems corrupt: {err}"))), + }; + } + Ok(records) + } + /// Select minions by trait criterias pub fn select(&self, traits: HashMap) -> Result, SysinspectError> { let minions = self.get_tree(DB_MINIONS)?; From c49526a6d4cad4ce9a84d7c4b369fcc2aeb7e895 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 15:05:21 +0100 Subject: [PATCH 16/44] Add alias to get system traits w/o logging --- libsysinspect/src/traits/mod.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libsysinspect/src/traits/mod.rs b/libsysinspect/src/traits/mod.rs index fc246f5a..e171464b 100644 --- a/libsysinspect/src/traits/mod.rs +++ b/libsysinspect/src/traits/mod.rs @@ -41,8 +41,7 @@ struct QueryParser; /// /// Each inner array should be treated with AND operator. pub fn parse_traits_query(input: &str) -> Result>, SysinspectError> { - let pairs = QueryParser::parse(Rule::expression, input) - .map_err(|err| SysinspectError::ModelDSLError(format!("Invalid query: {err}")))?; + let pairs = QueryParser::parse(Rule::expression, input).map_err(|err| SysinspectError::ModelDSLError(format!("Invalid query: {err}")))?; let mut out = Vec::new(); @@ -106,9 +105,19 @@ static _TRAITS: OnceCell = OnceCell::new(); /// Returns a copy of initialised traits. pub fn get_minion_traits(cfg: Option<&MinionConfig>) -> SystemTraits { + __get_minion_traits(cfg, false) +} + +/// Returns a copy of initialised traits. Same as get_minion_traits but without logging. +pub fn get_minion_traits_nolog(cfg: Option<&MinionConfig>) -> SystemTraits { + __get_minion_traits(cfg, true) +} + +/// Get or initialise system traits +fn __get_minion_traits(cfg: Option<&MinionConfig>, q: bool) -> SystemTraits { if let Some(cfg) = cfg { - return _TRAITS.get_or_init(|| SystemTraits::new(cfg.clone())).to_owned(); + return _TRAITS.get_or_init(|| SystemTraits::new(cfg.clone(), q)).to_owned(); } - _TRAITS.get_or_init(|| SystemTraits::new(MinionConfig::default())).to_owned() + _TRAITS.get_or_init(|| SystemTraits::new(MinionConfig::default(), q)).to_owned() } From 57b9e3d8b16922ea657c1d7698d848393aac688f Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 15:05:43 +0100 Subject: [PATCH 17/44] Display current minion traits to STDOUT --- sysminion/src/clidef.rs | 14 +++++++++++++- sysminion/src/main.rs | 4 ++++ sysminion/src/minion.rs | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/sysminion/src/clidef.rs b/sysminion/src/clidef.rs index b6283631..0c164d19 100644 --- a/sysminion/src/clidef.rs +++ b/sysminion/src/clidef.rs @@ -31,6 +31,7 @@ pub fn cli(version: &'static str, appname: &'static str) -> Command { ) .arg( Arg::new("start") + .short('s') .long("start") .conflicts_with("daemon") .action(ArgAction::SetTrue) @@ -38,17 +39,28 @@ pub fn cli(version: &'static str, appname: &'static str) -> Command { ) .arg( Arg::new("daemon") + .short('b') .long("daemon") + .alias("back") + .alias("background") .conflicts_with("start") .action(ArgAction::SetTrue) - .help("Start minion as a daemon") + .help("Start minion as a daemon in background") ) .arg( Arg::new("stop") + .short('k') .long("stop") .action(ArgAction::SetTrue) .help("Stop minion if runs as a daemon") ) + .arg( + Arg::new("info") + .short('i') + .long("info") + .action(ArgAction::SetTrue) + .help("Display minion info") + ) .next_help_heading("Minion") .subcommand(Command::new("setup").about("Minion local setup").styles(styles.clone()).disable_help_flag(true) diff --git a/sysminion/src/main.rs b/sysminion/src/main.rs index 577b0348..6602c145 100644 --- a/sysminion/src/main.rs +++ b/sysminion/src/main.rs @@ -18,6 +18,8 @@ use log::LevelFilter; use std::{env, fs::File, process::exit}; use tokio::task::JoinHandle; +use crate::minion::SysMinion; + static APPNAME: &str = "sysminion"; static VERSION: &str = "0.4.0"; static LOGGER: logger::STDOUTLogger = logger::STDOUTLogger; @@ -169,6 +171,8 @@ fn main() -> std::io::Result<()> { if let Err(err) = minion::launch_module(get_config(¶ms), sub) { log::error!("Error launching module: {err}"); } + } else if params.get_flag("info") { + SysMinion::print_info(&get_config(¶ms)); } else { cli.print_help()?; } diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index a6991ebe..5a3ef1ff 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -137,6 +137,28 @@ impl SysMinion { Ok(()) } + /// Display minion info + pub fn print_info(cfg: &MinionConfig) { + let mut out: IndexMap = IndexMap::new(); + let mut systraits = traits::get_minion_traits_nolog(Some(cfg)); + + systraits.put("minion.version".to_string(), json!(env!("CARGO_PKG_VERSION"))); + systraits.put("uri.master".to_string(), json!(cfg.master())); + systraits.put("uri.fileserver".to_string(), json!(cfg.fileserver())); + systraits.put("path.models".to_string(), json!(cfg.models_dir())); + systraits.put("path.functions".to_string(), json!(cfg.functions_dir())); + + for tk in systraits.trait_keys() { + out.insert(tk.to_owned(), dataconv::to_string(systraits.get(&tk)).unwrap_or_default()); + } + + let mut fmt = KeyValueFormatter::new(json!(out)); + fmt.set_key_title("Trait"); + fmt.set_value_title("Data"); + + println!("{}:\n\n{}", "Minion information".bright_green().bold(), fmt.format()); + } + async fn update_ping(&self) { let mut last_ping = self.last_ping.lock().await; *last_ping = Instant::now(); From f8fc1aee5b9d9ab4041027ec91e20a34ca2fcb88 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 15:15:13 +0100 Subject: [PATCH 18/44] Remove debug warning --- sysmaster/src/master.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 8cc6cd40..561cce25 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -246,8 +246,6 @@ impl SysMaster { msg.set_target(tgt); msg.set_retcode(ProtoErrorCode::Success); - log::warn!("Querying minion(s) with: {msg:#?}"); - return Some(msg); } From ed80d10e3b7a9e4804047abccc97924740352fd0 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 19:00:07 +0100 Subject: [PATCH 19/44] Add poor's man online minions lister. This will be removed in a future and integrated with the UI --- libsysinspect/src/proto/query.rs | 3 ++ src/clidef.rs | 6 ++++ src/main.rs | 8 ++++- sysmaster/src/clidef.rs | 9 ------ sysmaster/src/master.rs | 50 ++++++++++++++++++++++++++++---- sysmaster/src/registry/mreg.rs | 19 +++++++++++- 6 files changed, 79 insertions(+), 16 deletions(-) diff --git a/libsysinspect/src/proto/query.rs b/libsysinspect/src/proto/query.rs index 2d65e419..2975db67 100644 --- a/libsysinspect/src/proto/query.rs +++ b/libsysinspect/src/proto/query.rs @@ -23,6 +23,9 @@ pub mod commands { // Remove minion (unregister) pub const CLUSTER_REMOVE_MINION: &str = "cluster/minion/remove"; + + // Get online minions + pub const CLUSTER_ONLINE_MINIONS: &str = "cluster/minion/online"; } /// diff --git a/src/clidef.rs b/src/clidef.rs index 6273507f..e4338db3 100644 --- a/src/clidef.rs +++ b/src/clidef.rs @@ -105,6 +105,12 @@ pub fn cli(version: &'static str) -> Command { .long("unregister") .help("Unregister a minion by its System ID. A new registration will be required.") ) + .arg( + Arg::new("online") + .long("online") + .action(ArgAction::SetTrue) + .help("Show online minions in the cluster" ) + ) .arg( Arg::new("sync") .long("sync") diff --git a/src/main.rs b/src/main.rs index a8eb6499..dc4f3bf1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use libsysinspect::{ logger::{self, MemoryLogger}, proto::query::{ SCHEME_COMMAND, - commands::{CLUSTER_REMOVE_MINION, CLUSTER_SHUTDOWN, CLUSTER_SYNC}, + commands::{CLUSTER_ONLINE_MINIONS, CLUSTER_REMOVE_MINION, CLUSTER_SHUTDOWN, CLUSTER_SYNC}, }, reactor::handlers, traits::get_minion_traits, @@ -255,6 +255,12 @@ async fn main() { if let Err(err) = call_master_fifo(&format!("{SCHEME_COMMAND}{CLUSTER_REMOVE_MINION}"), "", None, Some(mid), &cfg.socket(), None) { log::error!("Cannot reach master: {err}"); } + } else if params.get_flag("online") { + if let Err(err) = call_master_fifo(&format!("{SCHEME_COMMAND}{CLUSTER_ONLINE_MINIONS}"), "", None, None, &cfg.socket(), None) { + log::error!("Cannot reach master: {err}"); + } else { + println!("Check the master's logs for online minions information. πŸ˜€"); + } } else if let Some(mpath) = params.get_one::("model") { let mut sr = SysInspectRunner::new(&MinionConfig::default()); sr.set_model_path(mpath); diff --git a/sysmaster/src/clidef.rs b/sysmaster/src/clidef.rs index c63aeb9f..7bbdb487 100644 --- a/sysmaster/src/clidef.rs +++ b/sysmaster/src/clidef.rs @@ -43,15 +43,6 @@ pub fn cli(version: &'static str, appname: &'static str) -> Command { .help("Stop master if it is in daemon mode") ) - .next_help_heading("Info") - .arg( - Arg::new("status") - .long("status") - .action(ArgAction::SetTrue) - .help("Show connected minions") - ) - - // Other .next_help_heading("Other") .arg( diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 561cce25..35b13279 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -8,6 +8,7 @@ use crate::{ }, telemetry::{otel::OtelLogger, rds::FunctionReducer}, }; +use colored::Colorize; use indexmap::IndexMap; use libeventreg::{ ipcs::DbIPCService, @@ -21,7 +22,10 @@ use libsysinspect::{ self, MasterMessage, MinionMessage, MinionTarget, ProtoConversion, errcodes::ProtoErrorCode, payload::ModStatePayload, - query::SCHEME_COMMAND, + query::{ + SCHEME_COMMAND, + commands::{CLUSTER_ONLINE_MINIONS, CLUSTER_REMOVE_MINION}, + }, rqtypes::{ProtoValue, RequestType}, }, util::{self, iofs::scan_files_sha256}, @@ -66,7 +70,6 @@ impl SysMaster { let (tx, _) = broadcast::channel::>(100); let mkr = MinionsKeyRegistry::new(cfg.minion_keys_root())?; let mreg = Arc::new(Mutex::new(MinionRegistry::new(cfg.minion_registry_root())?)); - let evtreg = Arc::new(Mutex::new(EventsRegistry::new(cfg.telemetry_location(), cfg.history())?)); let evtipc = Arc::new(DbIPCService::new(Arc::clone(&evtreg), cfg.telemetry_socket().to_str().unwrap_or_default())?); let vmc = VirtualMinionsCluster::new(cfg.cluster().to_owned(), Arc::clone(&mreg)); @@ -437,7 +440,7 @@ impl SysMaster { { Ok(sid) => sid, Err(err) => { - log::error!("Unable to acquire session for this iteration: {err}"); + log::debug!("Unable to acquire session for this iteration: {err}"); return; } }; @@ -477,7 +480,7 @@ impl SysMaster { let sid = match master.evtipc.get_session(&util::dataconv::as_str(pl.get("cid").cloned())).await { Ok(sid) => sid, Err(err) => { - log::error!("Unable to acquire session for this iteration: {err}"); + log::debug!("Unable to acquire session for this iteration: {err}"); return; } }; @@ -530,7 +533,8 @@ impl SysMaster { } pub async fn on_fifo_commands(&mut self, msg: &MasterMessage) { - if msg.target().scheme().eq("cmd://cluster/minion/remove") && !msg.target().id().is_empty() { + println!("Received command: {}", msg.target().scheme()); + if msg.target().scheme().eq(&format!("cmd://{}", CLUSTER_REMOVE_MINION)) && !msg.target().id().is_empty() { log::info!("Removing minion {}", msg.target().id()); if let Err(err) = self.mreg.lock().await.remove(msg.target().id()) { log::error!("Unable to remove minion {}: {err}", msg.target().id()); @@ -538,6 +542,42 @@ impl SysMaster { if let Err(err) = self.mkr().remove_mn_key(msg.target().id()) { log::error!("Unable to unregister minion: {err}"); } + } else if msg.target().scheme().eq(&format!("cmd://{}", CLUSTER_ONLINE_MINIONS)) { + // XXX: This is just a logdumper for now, because there is no proper response channel yet. + // Most likely we need to dump FIFO mechanism in a whole and replace with something else. + log::info!("Listing online minions"); + let mreg = self.mreg.lock().await; + let mut session = self.session.lock().await; + match mreg.get_registered_ids() { + Ok(ids) => { + let mut msg: Vec = vec![]; + for (idx, mid) in ids.iter().enumerate() { + let alive = session.alive(mid); + let traits = match mreg.get(mid) { + Ok(Some(mrec)) => mrec.get_traits().to_owned(), + _ => HashMap::new(), + }; + let mut h = traits.get("system.hostname.fqdn").and_then(|v| v.as_str()).unwrap_or("unknown"); + if h.is_empty() { + h = traits.get("system.hostname").and_then(|v| v.as_str()).unwrap_or("unknown"); + } + let ip = traits.get("system.hostname.ip").and_then(|v| v.as_str()).unwrap_or("unknown"); + + msg.push(format!( + "{}. {} ({}), ID: {} is {}", + idx + 1, + h.bright_yellow(), + ip.yellow(), + mid.cyan(), + if alive { "βœ…" } else { "❌" } + )); + } + log::info!("Status of all registered minions:\n{}", msg.join("\n")); + } + Err(err) => { + log::error!("Unable to get online minions: {err}"); + } + } } } diff --git a/sysmaster/src/registry/mreg.rs b/sysmaster/src/registry/mreg.rs index 8ae95e93..519c69bd 100644 --- a/sysmaster/src/registry/mreg.rs +++ b/sysmaster/src/registry/mreg.rs @@ -66,7 +66,24 @@ impl MinionRegistry { Ok(()) } - pub fn get(&mut self, mid: &str) -> Result, SysinspectError> { + pub fn get_registered_ids(&self) -> Result, SysinspectError> { + let minions = self.get_tree(DB_MINIONS)?; + let mut ids: Vec = Vec::new(); + + for entry in minions.iter() { + match entry { + Ok((k_ent, _v_ent)) => { + let mid = String::from_utf8(k_ent.to_vec()).unwrap_or_default(); + ids.push(mid); + } + Err(err) => return Err(SysinspectError::MasterGeneralError(format!("Minion database seems corrupt: {err}"))), + }; + } + + Ok(ids) + } + + pub fn get(&self, mid: &str) -> Result, SysinspectError> { let minions = self.get_tree(DB_MINIONS)?; let data = match minions.get(mid) { Ok(data) => data, From 3c2c3774d2329620d8b1266fd0b91a80ae3ccab0 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 19:08:02 +0100 Subject: [PATCH 20/44] Remove debug spam --- sysmaster/src/master.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 35b13279..155c6a06 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -533,7 +533,6 @@ impl SysMaster { } pub async fn on_fifo_commands(&mut self, msg: &MasterMessage) { - println!("Received command: {}", msg.target().scheme()); if msg.target().scheme().eq(&format!("cmd://{}", CLUSTER_REMOVE_MINION)) && !msg.target().id().is_empty() { log::info!("Removing minion {}", msg.target().id()); if let Err(err) = self.mreg.lock().await.remove(msg.target().id()) { From d9e5831da3308bb002c89540bbe82d5d281b5f38 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 17 Dec 2025 19:08:16 +0100 Subject: [PATCH 21/44] Upgrade dependencies --- Cargo.lock | 2175 +++++++++++++++--------- Cargo.toml | 12 +- libeventreg/Cargo.toml | 14 +- libmodcore/Cargo.toml | 8 +- libmodpak/Cargo.toml | 18 +- libscheduler/Cargo.toml | 4 +- libsetup/Cargo.toml | 8 +- libsysinspect/Cargo.toml | 30 +- libtelemetry/Cargo.toml | 18 +- libwebapi/Cargo.toml | 18 +- modules/fs/file/Cargo.toml | 6 +- modules/sys/net/Cargo.toml | 6 +- modules/sys/proc/Cargo.toml | 4 +- modules/sys/run/Cargo.toml | 4 +- modules/sys/ssrun/Cargo.toml | 6 +- sysclient/Cargo.toml | 10 +- sysclient/sysinspect-client/Cargo.toml | 2 +- sysmaster/Cargo.toml | 24 +- sysminion/Cargo.toml | 24 +- 19 files changed, 1448 insertions(+), 943 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1b6ad50..98b03f1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bytes", "futures-core", "futures-sink", @@ -21,23 +21,23 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.11.1" +version = "3.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cceded2fb55f3c4b67068fa64962e2ca59614edc5b03167de9ff82ae803da0" +checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", "base64", - "bitflags 2.9.4", + "bitflags 2.10.0", "brotli", "bytes", "bytestring", "derive_more", "encoding_rs", "flate2", - "foldhash", + "foldhash 0.1.5", "futures-core", "h2 0.3.27", "http 0.2.12", @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.11.0" +version = "4.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +checksum = "1654a77ba142e37f049637a3e5685f864514af11fcbc51cb51eb6596afe5b8d6" dependencies = [ "actix-codec", "actix-http", @@ -151,7 +151,7 @@ dependencies = [ "cookie", "derive_more", "encoding_rs", - "foldhash", + "foldhash 0.1.5", "futures-core", "futures-util", "impl-more", @@ -167,7 +167,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2 0.5.10", + "socket2 0.6.1", "time", "tracing", "url", @@ -182,16 +182,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", + "syn 2.0.111", ] [[package]] @@ -235,7 +226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -243,9 +234,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -282,9 +273,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -297,9 +288,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -312,29 +303,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arbitrary" @@ -357,6 +348,45 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.17", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -376,7 +406,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -387,7 +417,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -405,6 +435,36 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "attribute-derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05832cdddc8f2650cc2cc187cc2e952b8c133a48eb055f35211f61ee81502d77" +dependencies = [ + "attribute-derive-macro", + "derive-where", + "manyhow", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7cdbbd4bd005c5d3e2e9c885e6fa575db4f4a3572335b974d8db853b6beb61" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.111", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -413,21 +473,21 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.14.0" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b8ff6c09cd57b16da53641caa860168b88c172a5ee163b0288d3d6eea12786" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" dependencies = [ "aws-lc-sys", + "untrusted 0.7.1", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.31.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e44d16778acaf6a9ec9899b92cebd65580b83f685446bf2e1f5d3d732f99dcd" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" dependencies = [ - "bindgen 0.72.1", "cc", "cmake", "dunce", @@ -444,7 +504,7 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "itoa", @@ -470,7 +530,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "mime", @@ -481,21 +541,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base64" version = "0.22.1" @@ -504,9 +549,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bincode" @@ -523,7 +568,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -534,27 +579,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.106", -] - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.9.4", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.1", - "shlex", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -565,11 +590,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -613,9 +638,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" dependencies = [ "borsh-derive", "cfg_aliases", @@ -623,15 +648,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -657,9 +682,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "regex-automata", @@ -668,17 +693,18 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-unit" -version = "5.1.6" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" +checksum = "8c6d47a4e2961fb8721bcfc54feae6455f2f64e7054f9bc67e875f0e77f4c58d" dependencies = [ "rust_decimal", + "schemars 1.1.0", "serde", "utf8-width", ] @@ -707,9 +733,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -719,24 +745,24 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "bytestring" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" dependencies = [ "bytes", ] [[package]] name = "bzip2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" dependencies = [ "libbz2-rs-sys", ] @@ -776,9 +802,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.36" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -786,6 +812,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -797,9 +829,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -819,7 +851,7 @@ dependencies = [ "pure-rust-locales", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -830,7 +862,7 @@ checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" dependencies = [ "chrono", "chrono-tz-build", - "phf", + "phf 0.11.3", ] [[package]] @@ -840,7 +872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" dependencies = [ "parse-zoneinfo", - "phf", + "phf 0.11.3", "phf_codegen", ] @@ -862,23 +894,22 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading", ] [[package]] name = "clap" -version = "4.5.47" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.47" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -888,9 +919,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "clipboard-win" @@ -903,13 +934,19 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +checksum = "b042e5d8a74ae91bb0961acd039822472ec99f8ab0948cbf6d1369588f8be586" dependencies = [ "cc", ] +[[package]] +name = "collection_literals" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084" + [[package]] name = "colorchoice" version = "1.0.4" @@ -925,6 +962,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compact_str" version = "0.8.1" @@ -940,6 +987,20 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -952,6 +1013,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.16.2" @@ -973,6 +1043,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1037,10 +1117,10 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm_winapi", "mio", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rustix 0.38.44", "signal-hook", "signal-hook-mio", @@ -1074,9 +1154,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1084,21 +1164,21 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] name = "csv-core" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] @@ -1127,7 +1207,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1145,8 +1225,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -1160,7 +1250,21 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.111", ] [[package]] @@ -1169,9 +1273,20 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.111", ] [[package]] @@ -1185,9 +1300,15 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + [[package]] name = "der" version = "0.7.10" @@ -1195,18 +1316,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", - "pem-rfc7468", + "der_derive", + "flagset", + "pem-rfc7468 0.7.0", "zeroize", ] +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", +] + +[[package]] +name = "derive-where" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -1217,27 +1376,29 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 2.0.106", + "rustc_version", + "syn 2.0.111", "unicode-xid", ] @@ -1288,18 +1449,18 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "dns-lookup" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853d5bcf0b73bd5e6d945b976288621825c7166e9f06c5a035ae1aaf42d1b64f" +checksum = "6e39034cee21a2f5bbb66ba0e3689819c4bb5d00382a282006e802a7ffa6c41d" dependencies = [ "cfg-if", "libc", - "socket2 0.6.0", + "socket2 0.6.1", "windows-sys 0.60.2", ] @@ -1393,9 +1554,9 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -1433,7 +1594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.59.0", ] [[package]] @@ -1500,9 +1661,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.1" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fixedbitset" @@ -1510,11 +1671,17 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "libz-rs-sys", @@ -1533,6 +1700,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1647,7 +1820,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1715,14 +1888,37 @@ dependencies = [ "version_check", ] +[[package]] +name = "get-size-derive2" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab21d7bd2c625f2064f04ce54bcb88bc57c45724cde45cba326d784e22d3f71a" +dependencies = [ + "attribute-derive", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "get-size2" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879272b0de109e2b67b39fcfe3d25fdbba96ac07e44a254f5a0b4d7ff55340cb" +dependencies = [ + "compact_str 0.9.0", + "get-size-derive2", + "hashbrown 0.16.1", + "smallvec", +] + [[package]] name = "gethostname" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.2", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1742,29 +1938,23 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.5+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" version = "0.3.3" @@ -1790,7 +1980,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ignore", "walkdir", ] @@ -1818,7 +2008,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.1", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -1836,8 +2026,8 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.11.1", + "http 1.4.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -1846,12 +2036,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -1880,7 +2071,16 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", ] [[package]] @@ -1927,11 +2127,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1947,12 +2147,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1963,7 +2162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -1974,7 +2173,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body", "pin-project-lite", ] @@ -2018,16 +2217,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body", "httparse", "httpdate", @@ -2045,7 +2244,7 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", + "http 1.4.0", "hyper", "hyper-util", "rustls", @@ -2086,24 +2285,24 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", - "system-configuration", + "socket2 0.6.1", + "system-configuration 0.6.1", "tokio", "tower-service", "tracing", @@ -2135,9 +2334,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2159,9 +2358,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -2172,9 +2371,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -2185,11 +2384,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -2200,42 +2398,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -2282,9 +2476,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.23" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" dependencies = [ "crossbeam-deque", "globset", @@ -2315,20 +2509,24 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "inout" @@ -2342,15 +2540,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" +checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" dependencies = [ - "darling", + "darling 0.20.11", "indoc", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2362,6 +2560,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + [[package]] name = "interprocess" version = "2.2.3" @@ -2375,17 +2579,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipc-channel" version = "0.19.0" @@ -2415,9 +2608,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -2432,14 +2625,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", @@ -2448,9 +2641,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -2487,43 +2680,65 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", ] +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.78" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2539,7 +2754,7 @@ dependencies = [ "pest_derive", "regex", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -2595,33 +2810,28 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "lexical-parse-float" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" +checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" dependencies = [ "lexical-parse-integer", "lexical-util", - "static_assertions", ] [[package]] name = "lexical-parse-integer" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" +checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" dependencies = [ "lexical-util", - "static_assertions", ] [[package]] name = "lexical-util" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" -dependencies = [ - "static_assertions", -] +checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" [[package]] name = "lexopt" @@ -2637,9 +2847,9 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.176" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libeventreg" @@ -2652,7 +2862,7 @@ dependencies = [ "fs_extra", "glob", "hyper-util", - "indexmap 2.11.1", + "indexmap 2.12.1", "interprocess", "ipc-channel", "libsysinspect", @@ -2690,12 +2900,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.8" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-link", ] [[package]] @@ -2709,7 +2919,7 @@ name = "libmodcore" version = "0.1.0" dependencies = [ "colored", - "indexmap 2.11.1", + "indexmap 2.12.1", "libsysinspect", "regex", "serde", @@ -2730,7 +2940,7 @@ dependencies = [ "fs_extra", "glob", "goblin", - "indexmap 2.11.1", + "indexmap 2.12.1", "libmodcore", "libsysinspect", "log", @@ -2746,11 +2956,11 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", ] @@ -2828,7 +3038,7 @@ dependencies = [ "humantime", "humantime-serde", "if-addrs", - "indexmap 2.11.1", + "indexmap 2.12.1", "jsonpath-rust", "jsonpath_lib", "lazy_static", @@ -2855,7 +3065,7 @@ dependencies = [ "sysinfo 0.34.2", "tera", "textwrap", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "unicode-segmentation", "uuid", @@ -2869,7 +3079,7 @@ dependencies = [ "byte-unit", "colored", "globset", - "indexmap 2.11.1", + "indexmap 2.12.1", "jsonpath-rust", "libsysinspect", "log", @@ -2894,7 +3104,7 @@ dependencies = [ "base64", "colored", "hex", - "indexmap 2.11.1", + "indexmap 2.12.1", "libsysinspect", "log", "once_cell", @@ -2913,18 +3123,18 @@ dependencies = [ [[package]] name = "libz-rs-sys" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" +checksum = "15413ef615ad868d4d65dce091cb233b229419c7c0c4bcaa746c0901c49ff39c" dependencies = [ "zlib-rs", ] [[package]] name = "libz-sys" -version = "1.1.22" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" dependencies = [ "cc", "libc", @@ -2946,9 +3156,9 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "local-channel" @@ -2969,19 +3179,18 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru" @@ -2994,9 +3203,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.11.5" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" +checksum = "ab6473172471198271ff72e9379150e9dfd70d8e533e0752a27e515b48dd375e" dependencies = [ "twox-hash", ] @@ -3024,11 +3233,11 @@ dependencies = [ [[package]] name = "malachite-base" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c738d3789301e957a8f7519318fcbb1b92bb95863b28f6938ae5a05be6259f34" +checksum = "c0c91cb6071ed9ac48669d3c79bd2792db596c7e542dbadd217b385bb359f42d" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", "itertools 0.14.0", "libm", "ryu", @@ -3036,9 +3245,9 @@ dependencies = [ [[package]] name = "malachite-bigint" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f46b904a4725706c5ad0133b662c20b388a3ffb04bda5154029dcb0cd28ae34" +checksum = "7ff3af5010102f29f2ef4ee6f7b1c5b3f08a6c261b5164e01c41cf43772b6f90" dependencies = [ "malachite-base", "malachite-nz", @@ -3049,9 +3258,9 @@ dependencies = [ [[package]] name = "malachite-nz" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1707c9a1fa36ce21749b35972bfad17bbf34cf5a7c96897c0491da321e387d3b" +checksum = "1d9ecf4dd76246fd622de4811097966106aa43f9cd7cc36cb85e774fe84c8adc" dependencies = [ "itertools 0.14.0", "libm", @@ -3061,15 +3270,38 @@ dependencies = [ [[package]] name = "malachite-q" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d764801aa4e96bbb69b389dcd03b50075345131cd63ca2e380bca71cc37a3675" +checksum = "b7bc9d9adf5b0a7999d84f761c809bec3dc46fe983e4de547725d2b7730462a0" dependencies = [ "itertools 0.14.0", "malachite-base", "malachite-nz", ] +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + [[package]] name = "maplit" version = "1.0.2" @@ -3100,15 +3332,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -3163,18 +3395,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -3204,7 +3437,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -3265,7 +3498,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -3278,7 +3511,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -3306,20 +3539,29 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", ] [[package]] name = "num-bigint-dig" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ - "byteorder", "lazy_static", "libm", "num-integer", @@ -3387,9 +3629,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -3397,13 +3639,13 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3417,20 +3659,20 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] -name = "object" -version = "0.36.7" +name = "oid-registry" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ - "memchr", + "asn1-rs", ] [[package]] @@ -3441,17 +3683,17 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -3468,7 +3710,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3479,18 +3721,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.2+3.5.2" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -3509,7 +3751,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -3531,7 +3773,7 @@ checksum = "46d7ab32b827b5b495bd90fa95a6cb65ccc293555dcc3199ae2937d2d237c8ed" dependencies = [ "async-trait", "bytes", - "http 1.3.1", + "http 1.4.0", "opentelemetry", "reqwest", "tracing", @@ -3544,14 +3786,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d899720fe06916ccba71c01d04ecd77312734e2de3467fd30d9d580c8ce85656" dependencies = [ "futures-core", - "http 1.3.1", + "http 1.4.0", "opentelemetry", "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", "prost", "reqwest", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tonic", "tracing", @@ -3583,7 +3825,7 @@ dependencies = [ "percent-encoding", "rand 0.9.2", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -3635,7 +3877,7 @@ version = "1.0.0-alpha5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce9484729b3e52c0bacdc5191cb6a6a5f31ef4c09c5e4ab1209d3340ad9e997b" dependencies = [ - "bindgen 0.69.5", + "bindgen", "libc", ] @@ -3652,12 +3894,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -3676,15 +3918,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -3714,12 +3956,12 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ "base64", - "serde", + "serde_core", ] [[package]] @@ -3731,6 +3973,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -3739,20 +3990,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" dependencies = [ "memchr", - "thiserror 2.0.16", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" dependencies = [ "pest", "pest_generator", @@ -3760,22 +4010,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" dependencies = [ "pest", "sha2", @@ -3788,7 +4038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.11.1", + "indexmap 2.12.1", ] [[package]] @@ -3797,7 +4047,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared 0.13.1", + "serde", ] [[package]] @@ -3806,8 +4067,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -3816,10 +4077,33 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "phf_shared" version = "0.11.3" @@ -3829,6 +4113,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -3846,7 +4139,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3919,7 +4212,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3945,9 +4238,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -3974,7 +4267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4020,18 +4313,29 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -4042,7 +4346,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "chrono", "flate2", "hex", @@ -4056,7 +4360,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "chrono", "flate2", "procfs-core 0.18.0", @@ -4070,7 +4374,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "chrono", "hex", ] @@ -4081,7 +4385,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "chrono", "hex", ] @@ -4103,7 +4407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", - "itertools 0.14.0", + "itertools 0.12.1", "log", "multimap", "once_cell", @@ -4112,7 +4416,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.106", + "syn 2.0.111", "tempfile", ] @@ -4123,10 +4427,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4160,9 +4464,9 @@ dependencies = [ [[package]] name = "pure-rust-locales" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" +checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d" [[package]] name = "pymath" @@ -4175,11 +4479,33 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quote-use" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e" +dependencies = [ + "quote", + "quote-use-macros", +] + +[[package]] +name = "quote-use-macros" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35" dependencies = [ + "proc-macro-utils", "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -4307,7 +4633,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] @@ -4316,9 +4642,9 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cassowary", - "compact_str", + "compact_str 0.8.1", "crossterm", "indoc", "instability", @@ -4379,11 +4705,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -4399,29 +4725,29 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -4431,9 +4757,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -4442,15 +4768,15 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rend" @@ -4463,9 +4789,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ "base64", "bytes", @@ -4474,7 +4800,7 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -4522,7 +4848,7 @@ dependencies = [ "pmutil", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4535,7 +4861,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.16", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -4590,9 +4916,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" dependencies = [ "const-oid", "digest", @@ -4623,11 +4949,12 @@ dependencies = [ [[package]] name = "ruff_python_ast" version = "0.0.0" -source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +source = "git+https://github.com/astral-sh/ruff.git?rev=2bffef59665ce7d2630dfd72ee99846663660db8#2bffef59665ce7d2630dfd72ee99846663660db8" dependencies = [ "aho-corasick", - "bitflags 2.9.4", - "compact_str", + "bitflags 2.10.0", + "compact_str 0.9.0", + "get-size2", "is-macro", "itertools 0.14.0", "memchr", @@ -4635,16 +4962,18 @@ dependencies = [ "ruff_source_file", "ruff_text_size", "rustc-hash 2.1.1", + "thiserror 2.0.17", ] [[package]] name = "ruff_python_parser" version = "0.0.0" -source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +source = "git+https://github.com/astral-sh/ruff.git?rev=2bffef59665ce7d2630dfd72ee99846663660db8#2bffef59665ce7d2630dfd72ee99846663660db8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bstr", - "compact_str", + "compact_str 0.9.0", + "get-size2", "memchr", "ruff_python_ast", "ruff_python_trivia", @@ -4653,13 +4982,13 @@ dependencies = [ "static_assertions", "unicode-ident", "unicode-normalization", - "unicode_names2", + "unicode_names2 1.3.0", ] [[package]] name = "ruff_python_trivia" version = "0.0.0" -source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +source = "git+https://github.com/astral-sh/ruff.git?rev=2bffef59665ce7d2630dfd72ee99846663660db8#2bffef59665ce7d2630dfd72ee99846663660db8" dependencies = [ "itertools 0.14.0", "ruff_source_file", @@ -4670,7 +4999,7 @@ dependencies = [ [[package]] name = "ruff_source_file" version = "0.0.0" -source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +source = "git+https://github.com/astral-sh/ruff.git?rev=2bffef59665ce7d2630dfd72ee99846663660db8#2bffef59665ce7d2630dfd72ee99846663660db8" dependencies = [ "memchr", "ruff_text_size", @@ -4679,7 +5008,10 @@ dependencies = [ [[package]] name = "ruff_text_size" version = "0.0.0" -source = "git+https://github.com/astral-sh/ruff.git?tag=0.11.0#2cd25ef6410fb5fca96af1578728a3d828d2d53a" +source = "git+https://github.com/astral-sh/ruff.git?rev=2bffef59665ce7d2630dfd72ee99846663660db8#2bffef59665ce7d2630dfd72ee99846663660db8" +dependencies = [ + "get-size2", +] [[package]] name = "run" @@ -4698,7 +5030,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -4709,9 +5041,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.7.2" +version = "8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" +checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -4720,22 +5052,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.7.2" +version = "8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" +checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.106", + "syn 2.0.111", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.7.2" +version = "8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" +checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" dependencies = [ "sha2", "walkdir", @@ -4743,9 +5075,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.37.2" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" +checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" dependencies = [ "arrayvec", "borsh", @@ -4757,12 +5089,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "1.1.0" @@ -4784,13 +5110,22 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4803,18 +5138,18 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "aws-lc-rs", "log", @@ -4825,6 +5160,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", +] + [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -4836,29 +5183,56 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.5.1", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" -version = "0.103.5" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] name = "rustpython" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "cfg-if", "dirs-next", @@ -4878,11 +5252,11 @@ dependencies = [ [[package]] name = "rustpython-codegen" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "ahash 0.8.12", - "bitflags 2.9.4", - "indexmap 2.11.1", + "bitflags 2.10.0", + "indexmap 2.12.1", "itertools 0.14.0", "log", "malachite-bigint", @@ -4894,44 +5268,43 @@ dependencies = [ "rustpython-compiler-core", "rustpython-literal", "rustpython-wtf8", - "thiserror 2.0.16", - "unicode_names2", + "thiserror 2.0.17", + "unicode_names2 2.0.0", ] [[package]] name = "rustpython-common" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "ascii", - "bitflags 2.9.4", - "bstr", + "bitflags 2.10.0", "cfg-if", - "getrandom 0.3.3", + "getrandom 0.3.4", "itertools 0.14.0", "libc", "lock_api", "malachite-base", "malachite-bigint", "malachite-q", - "memchr", + "nix 0.30.1", "num-complex", "num-traits", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "radium 1.1.1", "rustpython-literal", "rustpython-wtf8", "siphasher", - "unicode_names2", + "unicode_names2 2.0.0", "widestring", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustpython-compiler" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "ruff_python_ast", "ruff_python_parser", @@ -4939,15 +5312,15 @@ dependencies = [ "ruff_text_size", "rustpython-codegen", "rustpython-compiler-core", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "rustpython-compiler-core" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "itertools 0.14.0", "lz4_flex", "malachite-bigint", @@ -4959,18 +5332,17 @@ dependencies = [ [[package]] name = "rustpython-derive" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ - "proc-macro2", "rustpython-compiler", "rustpython-derive-impl", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "rustpython-derive-impl" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "itertools 0.14.0", "maplit", @@ -4978,23 +5350,23 @@ dependencies = [ "quote", "rustpython-compiler-core", "rustpython-doc", - "syn 2.0.106", + "syn 2.0.111", "syn-ext", "textwrap", ] [[package]] name = "rustpython-doc" -version = "0.3.0" -source = "git+https://github.com/RustPython/__doc__?tag=0.3.0#8b62ce5d796d68a091969c9fa5406276cb483f79" +version = "0.4.0" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ - "once_cell", + "phf 0.13.1", ] [[package]] name = "rustpython-literal" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "hexf-parse", "is-macro", @@ -5007,7 +5379,7 @@ dependencies = [ [[package]] name = "rustpython-pylib" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "glob", "rustpython-compiler-core", @@ -5017,9 +5389,9 @@ dependencies = [ [[package]] name = "rustpython-sre_engine" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "num_enum", "optional", "rustpython-wtf8", @@ -5028,27 +5400,29 @@ dependencies = [ [[package]] name = "rustpython-stdlib" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "adler32", "ahash 0.8.12", "ascii", + "aws-lc-rs", "base64", "blake2", "bzip2", "cfg-if", + "chrono", "crc32fast", "crossbeam-utils", "csv-core", + "der", "digest", "dns-lookup", "dyn-clone", "flate2", "gethostname", "hex", - "indexmap 2.11.1", + "indexmap 2.12.1", "itertools 0.14.0", - "junction", "libc", "libz-rs-sys", "lzma-sys", @@ -5063,12 +5437,20 @@ dependencies = [ "num-integer", "num-traits", "num_enum", + "oid-registry", "page_size", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "paste", + "pem-rfc7468 1.0.0", + "phf 0.13.1", + "pkcs8", "pymath", "rand_core 0.9.3", "rustix 1.1.2", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-platform-verifier", "rustpython-common", "rustpython-derive", "rustpython-vm", @@ -5076,8 +5458,8 @@ dependencies = [ "sha-1", "sha2", "sha3", - "socket2 0.6.0", - "system-configuration", + "socket2 0.6.1", + "system-configuration 0.7.0", "termios", "ucd", "unic-char-property", @@ -5088,10 +5470,13 @@ dependencies = [ "unic-ucd-ident", "unicode-bidi-mirroring", "unicode-casing", - "unicode_names2", + "unicode_names2 2.0.0", "uuid", + "webpki-roots", "widestring", - "windows-sys 0.59.0", + "windows-sys 0.61.2", + "x509-cert", + "x509-parser", "xml", "xz2", ] @@ -5099,11 +5484,11 @@ dependencies = [ [[package]] name = "rustpython-vm" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "ahash 0.8.12", "ascii", - "bitflags 2.9.4", + "bitflags 2.10.0", "bstr", "caseless", "cfg-if", @@ -5112,11 +5497,11 @@ dependencies = [ "crossbeam-utils", "errno", "exitcode", - "getrandom 0.3.3", + "getrandom 0.3.4", "glob", "half", "hex", - "indexmap 2.11.1", + "indexmap 2.12.1", "is-macro", "itertools 0.14.0", "junction", @@ -5126,7 +5511,6 @@ dependencies = [ "log", "malachite-bigint", "memchr", - "memoffset", "nix 0.30.1", "num-complex", "num-integer", @@ -5135,7 +5519,7 @@ dependencies = [ "num_enum", "once_cell", "optional", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "paste", "result-like", "ruff_python_ast", @@ -5150,13 +5534,12 @@ dependencies = [ "rustpython-literal", "rustpython-sre_engine", "rustyline", - "schannel", "scoped-tls", "scopeguard", "static_assertions", "strum 0.27.2", "strum_macros 0.27.2", - "thiserror 2.0.16", + "thiserror 2.0.17", "thread_local", "timsort", "uname", @@ -5164,19 +5547,16 @@ dependencies = [ "unic-ucd-category", "unic-ucd-ident", "unicode-casing", - "unicode_names2", "wasm-bindgen", "which", "widestring", - "windows 0.52.0", - "windows-sys 0.59.0", - "winreg", + "windows-sys 0.61.2", ] [[package]] name = "rustpython-wtf8" version = "0.4.0" -source = "git+https://github.com/RustPython/RustPython.git#cc4ebe62569b921c88a52d17c8490fd043899fd7" +source = "git+https://github.com/RustPython/RustPython.git#a4c93dfbbfed868e3fe896463a39d401079a3160" dependencies = [ "ascii", "bstr", @@ -5192,11 +5572,11 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustyline" -version = "17.0.1" +version = "17.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6614df0b6d4cfb20d1d5e295332921793ce499af3ebc011bf1e393380e1e492" +checksum = "e902948a25149d50edc1a8e0141aad50f54e22ba83ff988cf8f7c9ef07f50564" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "clipboard-win", "fd-lock", @@ -5220,9 +5600,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "safe_arch" -version = "0.7.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +checksum = "629516c85c29fe757770fa03f2074cf1eac43d44c02a3de9fc2ef7b0e207dfdd" dependencies = [ "bytemuck", ] @@ -5251,7 +5631,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.2", ] [[package]] @@ -5268,9 +5648,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -5307,7 +5687,7 @@ checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5333,8 +5713,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.4", - "core-foundation", + "bitflags 2.10.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -5352,41 +5745,52 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.11.1", + "indexmap 2.12.1", "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -5397,16 +5801,16 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -5423,19 +5827,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.1", + "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.0.4", - "serde", - "serde_derive", + "schemars 1.1.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -5443,14 +5846,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ - "darling", + "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5459,7 +5862,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.1", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -5546,9 +5949,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -5557,9 +5960,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -5582,9 +5985,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simdutf8" @@ -5654,12 +6057,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5696,10 +6099,10 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f84d13b3b8a0d4e91a2629911e951db1bb8671512f5c09d7d4ba34500ba68c8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", "libssh2-sys", - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] @@ -5719,9 +6122,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -5766,7 +6169,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5778,7 +6181,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5800,9 +6203,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -5817,7 +6220,7 @@ checksum = "b126de4ef6c2a628a68609dd00733766c3b015894698a438ebdf374933fc31d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5837,7 +6240,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5889,7 +6292,7 @@ dependencies = [ "clap", "colored", "crossterm", - "indexmap 2.11.1", + "indexmap 2.12.1", "jsonpath_lib", "libeventreg", "libmodpak", @@ -5933,7 +6336,7 @@ dependencies = [ "ed25519-dalek", "futures", "globset", - "indexmap 2.11.1", + "indexmap 2.12.1", "libc", "libeventreg", "libmodpak", @@ -5967,7 +6370,7 @@ dependencies = [ "ed25519-dalek", "futures", "glob", - "indexmap 2.11.1", + "indexmap 2.12.1", "libc", "libmodpak", "libsetup", @@ -5995,8 +6398,19 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", - "core-foundation", + "bitflags 2.10.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6030,22 +6444,22 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.22.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.0", + "windows-sys 0.59.0", ] [[package]] name = "tera" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee" +checksum = "e8004bca281f2d32df3bacd59bc67b312cb4c70cea46cbd79dbe8ac5ed206722" dependencies = [ "chrono", "chrono-tz", @@ -6060,7 +6474,7 @@ dependencies = [ "serde", "serde_json", "slug", - "unic-segment", + "unicode-segmentation", ] [[package]] @@ -6117,11 +6531,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -6132,18 +6546,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6157,11 +6571,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.43" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", + "itoa", "libc", "num-conv", "num_threads", @@ -6195,9 +6610,9 @@ checksum = "639ce8ef6d2ba56be0383a94dd13b92138d58de44c62618303bb798fa92bdc00" [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -6218,35 +6633,53 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6261,9 +6694,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -6294,7 +6727,7 @@ dependencies = [ "rusqlite", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "tracing-subscriber", @@ -6303,9 +6736,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -6316,44 +6749,54 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "0.9.9+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "eb5238e643fc34a1d5d7e753e1532a91912d74b63b92b3ea51fde8d1b7bc79dd" dependencies = [ - "serde", + "indexmap 2.12.1", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.4+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.11.1", - "serde", - "serde_spanned", + "indexmap 2.12.1", "toml_datetime", - "toml_write", + "toml_parser", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_parser" +version = "1.0.5+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "a9cd6190959dce0994aa8970cd32ab116d1851ead27e866039acaf2524ce44fa" [[package]] name = "tonic" @@ -6368,7 +6811,7 @@ dependencies = [ "bytes", "flate2", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -6398,7 +6841,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6438,14 +6881,14 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body", "iri-string", "pin-project-lite", @@ -6468,9 +6911,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -6480,20 +6923,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -6512,9 +6955,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -6538,9 +6981,9 @@ checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd" @@ -6593,15 +7036,6 @@ dependencies = [ "unic-ucd-normal", ] -[[package]] -name = "unic-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" -dependencies = [ - "unic-ucd-segment", -] - [[package]] name = "unic-ucd-age" version = "0.9.0" @@ -6668,17 +7102,6 @@ dependencies = [ "unic-ucd-version", ] -[[package]] -name = "unic-ucd-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - [[package]] name = "unic-ucd-version" version = "0.9.0" @@ -6696,9 +7119,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi-mirroring" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" [[package]] name = "unicode-casing" @@ -6708,9 +7131,9 @@ checksum = "061dbb8cc7f108532b6087a0065eff575e892a4bcb503dc57323a197457cc202" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-linebreak" @@ -6720,9 +7143,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] @@ -6768,8 +7191,18 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd" dependencies = [ - "phf", - "unicode_names2_generator", + "phf 0.11.3", + "unicode_names2_generator 1.3.0", +] + +[[package]] +name = "unicode_names2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d189085656ca1203291e965444e7f6a2723fbdd1dd9f34f8482e79bafd8338a0" +dependencies = [ + "phf 0.11.3", + "unicode_names2_generator 2.0.0", ] [[package]] @@ -6784,12 +7217,28 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "unicode_names2_generator" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1262662dc96937c71115228ce2e1d30f41db71a7a45d3459e98783ef94052214" +dependencies = [ + "phf_codegen", + "rand 0.8.5", +] + [[package]] name = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -6830,9 +7279,9 @@ dependencies = [ [[package]] name = "utf8-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" +checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" [[package]] name = "utf8_iter" @@ -6852,7 +7301,7 @@ version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" dependencies = [ - "indexmap 2.11.1", + "indexmap 2.12.1", "serde", "serde_json", "utoipa-gen", @@ -6867,7 +7316,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6897,14 +7346,14 @@ checksum = "e2eebbbfe4093922c2b6734d7c679ebfebd704a0d7e56dfcb0d05818ce28977d" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "atomic", - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -6951,29 +7400,20 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.5+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" -version = "1.0.0+wasi-0.2.4" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.101" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -6982,25 +7422,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.51" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -7011,9 +7437,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.101" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7021,36 +7447,54 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.101" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.101" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.78" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "8.0.0" @@ -7064,9 +7508,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.33" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +checksum = "13ca908d26e4786149c48efcf6c0ea09ab0e06d1fe3c17dc1b4b0f1ca4a7e788" dependencies = [ "bytemuck", "safe_arch", @@ -7074,9 +7518,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -7100,7 +7544,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.59.0", ] [[package]] @@ -7109,16 +7553,6 @@ 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 0.52.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.57.0" @@ -7139,15 +7573,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.57.0" @@ -7181,7 +7606,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7192,7 +7617,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7203,7 +7628,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7214,30 +7639,24 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-link", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -7260,11 +7679,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -7279,11 +7698,20 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-link 0.1.3", + "windows-targets 0.42.2", ] [[package]] @@ -7310,16 +7738,31 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.0" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows-link 0.2.0", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -7340,21 +7783,27 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -7363,9 +7812,15 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -7375,9 +7830,15 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -7387,9 +7848,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -7399,9 +7860,15 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -7411,9 +7878,15 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -7423,9 +7896,15 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -7435,9 +7914,15 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -7447,34 +7932,24 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" -dependencies = [ - "cfg-if", - "windows-sys 0.59.0", -] - [[package]] name = "winresource" -version = "0.1.23" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcacf11b6f48dd21b9ba002f991bdd5de29b2da8cc2800412f4b80f677e4957" +checksum = "6b021990998587d4438bb672b5c5f034cbc927f51b45e3807ab7323645ef4899" dependencies = [ "toml", "version_check", @@ -7488,15 +7963,15 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "wit-bindgen" -version = "0.45.1" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -7507,11 +7982,42 @@ dependencies = [ "tap", ] +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "sha1", + "signature 2.2.0", + "spki", + "tls_codec", +] + +[[package]] +name = "x509-parser" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3e137310115a65136898d2079f003ce33331a6c4b0d51f1531d1be082b6425" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.17", + "time", +] + [[package]] name = "xml" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e6e0a83ae73d886ab66fc2f82b598fbbb8f373357d5f2f9f783e50e4d06435" +checksum = "2df5825faced2427b2da74d9100f1e2e93c533fff063506a81ede1cf517b2e7e" [[package]] name = "xz2" @@ -7524,11 +8030,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -7536,34 +8041,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7583,15 +8088,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -7604,14 +8109,14 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -7620,9 +8125,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -7631,13 +8136,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -7649,22 +8154,22 @@ dependencies = [ "arbitrary", "crc32fast", "flate2", - "indexmap 2.11.1", + "indexmap 2.12.1", "memchr", "zopfli", ] [[package]] name = "zlib-rs" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" +checksum = "51f936044d677be1a1168fae1d03b583a285a5dd9d8cbf7b24c23aa1fc775235" [[package]] name = "zopfli" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" dependencies = [ "bumpalo", "crc32fast", diff --git a/Cargo.toml b/Cargo.toml index cc03dc61..89f8b263 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,14 +7,14 @@ edition = "2024" [dependencies] chrono = "0.4.42" -clap = { version = "4.5.47", features = ["unstable-styles"] } +clap = { version = "4.5.53", features = ["unstable-styles"] } colored = "3.0.0" libsysinspect = { path = "./libsysinspect" } libeventreg = { path = "./libeventreg" } libmodpak = { path = "./libmodpak" } -log = "0.4.28" +log = "0.4.29" sysinfo = { version = "0.33.1", features = ["linux-tmpfs"] } -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } ratatui = { version = "0.29.0", features = [ "all-widgets", "serde", @@ -22,10 +22,10 @@ ratatui = { version = "0.29.0", features = [ ] } crossterm = "0.28.1" rand = "0.9.2" -indexmap = "2.11.1" -serde_json = "1.0.143" +indexmap = "2.12.1" +serde_json = "1.0.145" jsonpath_lib = "0.3.0" -openssl = { version = "0.10.73", features = ["vendored"] } +openssl = { version = "0.10.75", features = ["vendored"] } [workspace] resolver = "2" diff --git a/libeventreg/Cargo.toml b/libeventreg/Cargo.toml index a346016a..a927a4d2 100644 --- a/libeventreg/Cargo.toml +++ b/libeventreg/Cargo.toml @@ -5,27 +5,27 @@ edition = "2024" [dependencies] chrono = "0.4.42" -serde = "1.0.219" -serde_json = "1.0.143" +serde = "1.0.228" +serde_json = "1.0.145" sled = "0.34.7" libsysinspect = { path = "../libsysinspect" } -log = "0.4.28" +log = "0.4.29" colored = "3.0.0" fs_extra = "1.3.0" -tempfile = "3.22.0" +tempfile = "3.23.0" glob = "0.3.3" async-stream = "0.3.6" bincode = "1.3.3" -hyper-util = { version = "0.1.16", features = ["tokio"] } +hyper-util = { version = "0.1.19", features = ["tokio"] } interprocess = "2.2.3" ipc-channel = { version = "0.19.0", features = ["async"] } prost = "0.13.5" rng = "0.1.0" -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } tonic = "0.12.3" tonic-build = "0.12.3" tower = "0.5.2" -indexmap = { version = "2.11.1", features = ["serde"] } +indexmap = { version = "2.12.1", features = ["serde"] } [build-dependencies] tonic-build = "0.12" diff --git a/libmodcore/Cargo.toml b/libmodcore/Cargo.toml index 0e6d3aa2..9ce5e4d4 100644 --- a/libmodcore/Cargo.toml +++ b/libmodcore/Cargo.toml @@ -5,10 +5,10 @@ edition = "2024" [dependencies] colored = "3.0.0" -indexmap = "2.11.1" -regex = "1.11.2" -serde = "1.0.219" -serde_json = "1.0.143" +indexmap = "2.12.1" +regex = "1.12.2" +serde = "1.0.228" +serde_json = "1.0.145" serde_yaml = "0.9.34" shlex = "1.3.0" textwrap = "0.16.2" diff --git a/libmodpak/Cargo.toml b/libmodpak/Cargo.toml index 2629ca67..8878d573 100644 --- a/libmodpak/Cargo.toml +++ b/libmodpak/Cargo.toml @@ -4,22 +4,22 @@ version = "0.1.0" edition = "2024" [dependencies] -serde = "1.0.219" -serde_json = "1.0.143" +serde = "1.0.228" +serde_json = "1.0.145" serde_yaml = "0.9.34" libsysinspect = { path = "../libsysinspect" } libmodcore = { path = "../libmodcore" } -log = "0.4.28" -clap = { version = "4.5.47", features = ["unstable-styles"] } +log = "0.4.29" +clap = { version = "4.5.53", features = ["unstable-styles"] } goblin = "0.9.3" colored = "3.0.0" -indexmap = { version = "2.11.1", features = ["serde"] } -reqwest = "0.12.23" +indexmap = { version = "2.12.1", features = ["serde"] } +reqwest = "0.12.26" fs_extra = "1.3.0" -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } once_cell = "1.21.3" -anyhow = "1.0.99" +anyhow = "1.0.100" cruet = "0.15.0" prettytable = "0.10.0" glob = "0.3.3" -regex = "1.11.2" +regex = "1.12.2" diff --git a/libscheduler/Cargo.toml b/libscheduler/Cargo.toml index 4c35e0b3..678de30b 100644 --- a/libscheduler/Cargo.toml +++ b/libscheduler/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } tokio-task-scheduler = "1.0.0" libsysinspect = { path = "../libsysinspect" } -log = "0.4.28" +log = "0.4.29" diff --git a/libsetup/Cargo.toml b/libsetup/Cargo.toml index 1575b0e5..6c3c2593 100644 --- a/libsetup/Cargo.toml +++ b/libsetup/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] libsysinspect = { path = "../libsysinspect" } -log = "0.4.28" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" +log = "0.4.29" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" serde_yaml = "0.9.34" -uuid = { version = "1.18.1", features = ["v4"] } +uuid = { version = "1.19.0", features = ["v4"] } libc = { version = "0.2", features = ["extra_traits"] } diff --git a/libsysinspect/Cargo.toml b/libsysinspect/Cargo.toml index 3ff87ac9..bd7866cd 100644 --- a/libsysinspect/Cargo.toml +++ b/libsysinspect/Cargo.toml @@ -8,18 +8,18 @@ base64 = "0.22.1" chrono = { version = "0.4.42", features = ["serde"] } colored = "3.0.0" hex = "0.4.3" -indexmap = { version = "2.11.1", features = ["serde"] } +indexmap = { version = "2.12.1", features = ["serde"] } lazy_static = "1.5.0" -log = "0.4.28" +log = "0.4.29" nix = { version = "0.29.0", features = ["net", "user"] } once_cell = "1.21.3" -pem = "3.0.5" -pest = "2.8.1" -pest_derive = "2.8.1" +pem = "3.0.6" +pest = "2.8.4" +pest_derive = "2.8.4" prettytable-rs = "0.10.0" rand = "0.8.5" -regex = "1.11.2" -rsa = { version = "0.9.8", features = ["pkcs5", "sha1", "sha2"] } +regex = "1.12.2" +rsa = { version = "0.9.9", features = ["pkcs5", "sha1", "sha2"] } #rustpython = { path = "../../RustPython", features = ["freeze-stdlib"] } rustpython-pylib = { git = "https://github.com/RustPython/RustPython.git", features = [ "freeze-stdlib", @@ -30,26 +30,26 @@ rustpython = { git = "https://github.com/RustPython/RustPython.git", features = rustpython-vm = { git = "https://github.com/RustPython/RustPython.git", features = [ "freeze-stdlib", ] } -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" serde_yaml = "0.9.34" sha2 = "0.10.9" sysinfo = { version = "0.34.2", features = ["linux-tmpfs"] } -tera = { version = "1.20.0", features = ["preserve_order", "date-locale"] } +tera = { version = "1.20.1", features = ["preserve_order", "date-locale"] } textwrap = { version = "0.16.2", features = ["hyphenation", "terminal_size"] } -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } unicode-segmentation = "1.12.0" -uuid = { version = "1.18.1", features = ["v4"] } +uuid = { version = "1.19.0", features = ["v4"] } walkdir = "2.5.0" shlex = "1.3.0" async-trait = "0.1.89" dashmap = "6.1.0" sled = "0.34.7" if-addrs = "0.13.4" -anyhow = "1.0.99" -thiserror = "2.0.16" +anyhow = "1.0.100" +thiserror = "2.0.17" jsonpath_lib = "0.3.0" jsonpath-rust = "1.0.4" humantime = "2.3.0" humantime-serde = "1.1.1" -libc = "0.2.176" +libc = "0.2.178" diff --git a/libtelemetry/Cargo.toml b/libtelemetry/Cargo.toml index f4397d9a..00bd40c3 100644 --- a/libtelemetry/Cargo.toml +++ b/libtelemetry/Cargo.toml @@ -4,20 +4,20 @@ version = "0.1.0" edition = "2024" [dependencies] -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" -tokio = { version = "1.47.1", features = ["full"] } -tracing = "0.1.41" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +tokio = { version = "1.48.0", features = ["full"] } +tracing = "0.1.43" libsysinspect = { path = "../libsysinspect" } -log = "0.4.28" +log = "0.4.29" opentelemetry-otlp = { version = "0.29.0", features = ["grpc-tonic", "gzip-tonic", "reqwest", "tonic", "zstd-tonic"] } opentelemetry = { version = "0.29.1", features = ["metrics"] } opentelemetry_sdk = { version = "0.29.0", features = ["metrics", "rt-tokio"] } opentelemetry-appender-log = "0.29.0" -byte-unit = { version = "5.1.6", features = ["serde"] } -regex = "1.11.2" -globset = "0.4.16" +byte-unit = { version = "5.2.0", features = ["serde"] } +regex = "1.12.2" +globset = "0.4.18" jsonpath-rust = "1.0.4" -indexmap = { version = "2.11.1", features = ["serde"] } +indexmap = { version = "2.12.1", features = ["serde"] } strfmt = "0.2.5" colored = "3.0.0" diff --git a/libwebapi/Cargo.toml b/libwebapi/Cargo.toml index f0b7f718..765a4d32 100644 --- a/libwebapi/Cargo.toml +++ b/libwebapi/Cargo.toml @@ -4,23 +4,23 @@ version = "0.1.0" edition = "2024" [dependencies] -actix-web = "4.11.0" +actix-web = "4.12.1" hex = "0.4.3" -reqwest = { version = "0.12.23", features = ["blocking", "json"] } -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" +reqwest = { version = "0.12.26", features = ["blocking", "json"] } +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" sodiumoxide = "0.2.7" -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } once_cell = "1.21.3" -log = "0.4.28" +log = "0.4.29" libsysinspect = { path = "../libsysinspect" } async-trait = "0.1.89" utoipa-swagger-ui = { version = "9.0.2", features = ["actix-web", "vendored", "debug-embed"] } utoipa = { version = "5.4.0", features = ["actix_extras"] } pam = "0.8.0" -uuid = "1.18.1" +uuid = "1.19.0" base64 = "0.22.1" -rsa = "0.9.8" +rsa = "0.9.9" colored = "3.0.0" serde_yaml = "0.9.34" -indexmap = { version = "2.11.1", features = ["serde"] } +indexmap = { version = "2.12.1", features = ["serde"] } diff --git a/modules/fs/file/Cargo.toml b/modules/fs/file/Cargo.toml index b29b4a4a..a5288a87 100644 --- a/modules/fs/file/Cargo.toml +++ b/modules/fs/file/Cargo.toml @@ -7,9 +7,9 @@ edition = "2024" chrono = "0.4.42" libsysinspect = { path = "../../../libsysinspect" } libmodcore = { path = "../../../libmodcore" } -reqwest = { version = "0.12.23", features = ["blocking"] } -serde = "1.0.219" -serde_json = "1.0.143" +reqwest = { version = "0.12.26", features = ["blocking"] } +serde = "1.0.228" +serde_json = "1.0.145" serde_yaml = "0.9.34" sha2 = "0.10.9" users = "0.11.0" diff --git a/modules/sys/net/Cargo.toml b/modules/sys/net/Cargo.toml index aad561a6..c4f9c451 100644 --- a/modules/sys/net/Cargo.toml +++ b/modules/sys/net/Cargo.toml @@ -8,8 +8,8 @@ libsysinspect = { path = "../../../libsysinspect" } libmodcore = { path = "../../../libmodcore" } neli = { version = "0.6.5", features = ["async", "netfilter", "tokio"] } nix = { version = "0.29.0", features = ["net"] } -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" serde_yaml = "0.9.34" sysinfo = { version = "0.32.1", features = ["linux-netdevs", "linux-tmpfs"] } -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } diff --git a/modules/sys/proc/Cargo.toml b/modules/sys/proc/Cargo.toml index 3c8684ab..62bf3b0e 100644 --- a/modules/sys/proc/Cargo.toml +++ b/modules/sys/proc/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" libsysinspect = { path = "../../../libsysinspect" } libmodcore = { path = "../../../libmodcore" } procfs = "0.17.0" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" serde_yaml = "0.9.34" sysinfo = { version = "0.32.1", features = ["linux-netdevs", "linux-tmpfs"] } diff --git a/modules/sys/run/Cargo.toml b/modules/sys/run/Cargo.toml index edbb11a6..41dece43 100644 --- a/modules/sys/run/Cargo.toml +++ b/modules/sys/run/Cargo.toml @@ -6,6 +6,6 @@ edition = "2024" [dependencies] libsysinspect = { path = "../../../libsysinspect" } libmodcore = { path = "../../../libmodcore" } -serde = "1.0.219" -serde_json = "1.0.143" +serde = "1.0.228" +serde_json = "1.0.145" serde_yaml = "0.9.34" diff --git a/modules/sys/ssrun/Cargo.toml b/modules/sys/ssrun/Cargo.toml index c04dc8cd..7cadfd02 100644 --- a/modules/sys/ssrun/Cargo.toml +++ b/modules/sys/ssrun/Cargo.toml @@ -6,10 +6,10 @@ edition = "2024" [dependencies] libsysinspect = { path = "../../../libsysinspect" } libmodcore = { path = "../../../libmodcore" } -anyhow = "1.0.99" +anyhow = "1.0.100" dotenv = "0.15.0" ssh2 = { version = "0.9.5", features = ["vendored-openssl"] } -serde = "1.0.219" -serde_json = "1.0.143" +serde = "1.0.228" +serde_json = "1.0.145" serde_yaml = "0.9.34" maplit = "1.0.2" diff --git a/sysclient/Cargo.toml b/sysclient/Cargo.toml index 81418c3c..f1c5bb16 100644 --- a/sysclient/Cargo.toml +++ b/sysclient/Cargo.toml @@ -6,12 +6,12 @@ edition = "2024" [dependencies] syswebclient = {path = "./sysinspect-client"} # That generated stuff, just don't touch it libsysinspect = { path = "../libsysinspect" } -tokio = { version = "1.47.1", features = ["full"] } -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" +tokio = { version = "1.48.0", features = ["full"] } +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" base64 = "0.22.1" rpassword = "7.4.0" -log = "0.4.28" -reqwest = { version = "0.12.23", features = ["blocking", "json"] } +log = "0.4.29" +reqwest = { version = "0.12.26", features = ["blocking", "json"] } hex = "0.4.3" sodiumoxide = "0.2.7" diff --git a/sysclient/sysinspect-client/Cargo.toml b/sysclient/sysinspect-client/Cargo.toml index 6c3dd965..77cf660d 100644 --- a/sysclient/sysinspect-client/Cargo.toml +++ b/sysclient/sysinspect-client/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] serde = { version = "^1.0", features = ["derive"] } -serde_with = { version = "^3.14", default-features = false, features = ["base64", "std", "macros"] } +serde_with = { version = "^3.16", default-features = false, features = ["base64", "std", "macros"] } serde_json = "^1.0" serde_repr = "^0.1" url = "^2.5" diff --git a/sysmaster/Cargo.toml b/sysmaster/Cargo.toml index be0edfe3..cf2c1608 100644 --- a/sysmaster/Cargo.toml +++ b/sysmaster/Cargo.toml @@ -4,7 +4,7 @@ version = "0.4.0" edition = "2024" [dependencies] -clap = { version = "4.5.47", features = ["unstable-styles"] } +clap = { version = "4.5.53", features = ["unstable-styles"] } colored = "3.0.0" ed25519-dalek = { version = "2.2.0", features = [ "asm", @@ -18,29 +18,29 @@ ed25519-dalek = { version = "2.2.0", features = [ "serde", ] } futures = "0.3.31" -libc = "0.2.175" +libc = "0.2.178" rand = "0.9.2" -rustls = "0.23.31" +rustls = "0.23.35" rustls-pemfile = "2.2.0" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" serde_yaml = "0.9.34" -tokio = { version = "1.47.1", features = ["full"] } -tokio-rustls = "0.26.2" +tokio = { version = "1.48.0", features = ["full"] } +tokio-rustls = "0.26.4" libsysinspect = { path = "../libsysinspect" } libeventreg = { path = "../libeventreg" } libmodpak = { path = "../libmodpak" } libscheduler = { path = "../libscheduler" } libtelemetry = { path = "../libtelemetry" } libwebapi = { path = "../libwebapi" } -log = "0.4.28" +log = "0.4.29" sled = "0.34.7" -rsa = { version = "0.9.8", features = ["pkcs5", "sha1", "sha2"] } -uuid = { version = "1.18.1", features = ["v4"] } -actix-web = "4.11.0" +rsa = { version = "0.9.9", features = ["pkcs5", "sha1", "sha2"] } +uuid = { version = "1.19.0", features = ["v4"] } +actix-web = "4.12.1" once_cell = "1.21.3" daemonize = "0.5.0" -indexmap = { version = "2.11.1", features = ["serde"] } +indexmap = { version = "2.12.1", features = ["serde"] } chrono = { version = "0.4.42", features = ["serde"] } async-trait = "0.1.89" globset = "0.4.18" diff --git a/sysminion/Cargo.toml b/sysminion/Cargo.toml index a9ce732f..fc0ee795 100644 --- a/sysminion/Cargo.toml +++ b/sysminion/Cargo.toml @@ -4,7 +4,7 @@ version = "0.4.0" edition = "2024" [dependencies] -clap = { version = "4.5.47", features = ["unstable-styles"] } +clap = { version = "4.5.53", features = ["unstable-styles"] } colored = "3.0.0" ed25519-dalek = { version = "2.2.0", features = [ "asm", @@ -18,26 +18,26 @@ ed25519-dalek = { version = "2.2.0", features = [ "serde", ] } rand = "0.9.2" -rustls = "0.23.31" +rustls = "0.23.35" rustls-pemfile = "2.2.0" -tokio = { version = "1.47.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } libsysinspect = { path = "../libsysinspect" } libsetup = { path = "../libsetup" } libmodpak = { path = "../libmodpak" } -log = "0.4.28" +log = "0.4.29" serde_yaml = "0.9.34" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.143" -indexmap = "2.11.1" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +indexmap = "2.12.1" once_cell = "1.21.3" sysinfo = { version = "0.34.2", features = ["linux-tmpfs"] } -rsa = { version = "0.9.8", features = ["pkcs5", "sha1", "sha2"] } -uuid = { version = "1.18.1", features = ["v4"] } -reqwest = "0.12.23" -regex = "1.11.2" +rsa = { version = "0.9.9", features = ["pkcs5", "sha1", "sha2"] } +uuid = { version = "1.19.0", features = ["v4"] } +reqwest = "0.12.26" +regex = "1.12.2" glob = "0.3.3" daemonize = "0.5.0" -libc = "0.2.175" +libc = "0.2.178" async-trait = "0.1.89" futures = "0.3.31" procfs = { version = "0.18.0", features = ["serde"] } From b09d1ae9c0651f0ed1b2ddb03ff441f99b299cce Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 18 Dec 2025 13:27:48 +0100 Subject: [PATCH 22/44] Add console crate to operate over ANSI data --- Cargo.lock | 32 +++++++++++++++++++++++--------- libsysinspect/Cargo.toml | 1 + 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98b03f1a..fad33906 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1001,6 +1001,19 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "console" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.61.2", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -1594,7 +1607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2636,7 +2649,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3033,6 +3046,7 @@ dependencies = [ "base64", "chrono", "colored", + "console", "dashmap", "hex", "humantime", @@ -3543,7 +3557,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4407,7 +4421,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", - "itertools 0.12.1", + "itertools 0.14.0", "log", "multimap", "once_cell", @@ -4427,7 +4441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.111", @@ -5142,7 +5156,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5208,7 +5222,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6452,7 +6466,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7544,7 +7558,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/libsysinspect/Cargo.toml b/libsysinspect/Cargo.toml index bd7866cd..797e562d 100644 --- a/libsysinspect/Cargo.toml +++ b/libsysinspect/Cargo.toml @@ -53,3 +53,4 @@ jsonpath-rust = "1.0.4" humantime = "2.3.0" humantime-serde = "1.1.1" libc = "0.2.178" +console = "0.16.2" From aca7a026afaf060c9264173378c7473c595f02ef Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 18 Dec 2025 13:28:32 +0100 Subject: [PATCH 23/44] Add option to strip ANSI colors from logs, if not working on terminal or explicitly --- libsysinspect/src/logger.rs | 20 +++++++++++++++++--- src/main.rs | 13 ++++++++----- sysmaster/src/clidef.rs | 6 ++++++ sysmaster/src/main.rs | 8 ++++---- sysminion/src/clidef.rs | 6 ++++++ sysminion/src/main.rs | 6 +++--- 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/libsysinspect/src/logger.rs b/libsysinspect/src/logger.rs index 5b2e92b6..4afb9161 100644 --- a/libsysinspect/src/logger.rs +++ b/libsysinspect/src/logger.rs @@ -1,10 +1,20 @@ -use std::sync::Mutex; +use std::{io::IsTerminal, sync::Mutex}; use chrono::Local; use colored::{self, Colorize}; +use console::strip_ansi_codes; use log::{Level, Metadata, Record}; -pub struct STDOUTLogger; +#[derive(Default)] +pub struct STDOUTLogger { + nocolor: bool, +} + +impl STDOUTLogger { + pub fn new(nocolor: bool) -> STDOUTLogger { + STDOUTLogger { nocolor } + } +} impl log::Log for STDOUTLogger { fn enabled(&self, metadata: &Metadata) -> bool { @@ -21,7 +31,11 @@ impl log::Log for STDOUTLogger { log::Level::Trace => format!("{}", msg.level().as_str().cyan()), }; - println!("[{}] - {}: {}", Local::now().format("%d/%m/%Y %H:%M:%S"), s_level, msg.args()); + let mut msg = format!("[{}] - {}: {}", Local::now().format("%d/%m/%Y %H:%M:%S"), s_level, msg.args()); + if self.nocolor || !std::io::stdout().is_terminal() { + msg = strip_ansi_codes(msg.as_str()).into_owned(); + } + println!("{}", msg); } } diff --git a/src/main.rs b/src/main.rs index dc4f3bf1..5331c6b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use libsysinspect::{ select_config_path, }, inspector::SysInspectRunner, - logger::{self, MemoryLogger}, + logger::{self, MemoryLogger, STDOUTLogger}, proto::query::{ SCHEME_COMMAND, commands::{CLUSTER_ONLINE_MINIONS, CLUSTER_REMOVE_MINION, CLUSTER_SHUTDOWN, CLUSTER_SYNC}, @@ -23,14 +23,14 @@ use std::{ io::{ErrorKind, Write}, path::PathBuf, process::exit, - sync::Mutex, + sync::{Mutex, OnceLock}, }; mod clidef; mod ui; static VERSION: &str = "0.4.0"; -static LOGGER: logger::STDOUTLogger = logger::STDOUTLogger; +static LOGGER: OnceLock = OnceLock::new(); static MEM_LOGGER: MemoryLogger = MemoryLogger { messages: Mutex::new(Vec::new()) }; /// Display event handlers @@ -57,8 +57,11 @@ fn call_master_fifo( /// Set logger fn set_logger(p: &ArgMatches) { - let log: &'static dyn log::Log = - if *p.get_one::("ui").unwrap_or(&false) { &MEM_LOGGER as &'static dyn log::Log } else { &LOGGER as &'static dyn log::Log }; + let log: &'static dyn log::Log = if *p.get_one::("ui").unwrap_or(&false) { + &MEM_LOGGER as &'static dyn log::Log + } else { + LOGGER.get_or_init(|| STDOUTLogger::default()) as &'static dyn log::Log + }; if let Err(err) = log::set_logger(log).map(|()| { log::set_max_level(match p.get_count("debug") { diff --git a/sysmaster/src/clidef.rs b/sysmaster/src/clidef.rs index 7bbdb487..6d49e791 100644 --- a/sysmaster/src/clidef.rs +++ b/sysmaster/src/clidef.rs @@ -52,6 +52,12 @@ pub fn cli(version: &'static str, appname: &'static str) -> Command { .action(ArgAction::Count) .help("Set debug mode for more verbose output. Increase this flag for more verbosity."), ) + .arg( + Arg::new("no-color") + .long("no-color") + .action(ArgAction::SetTrue) + .help("Disable colored output in logs"), + ) .arg( Arg::new("help") .short('h') diff --git a/sysmaster/src/main.rs b/sysmaster/src/main.rs index 05ab8e39..acf9b9f2 100644 --- a/sysmaster/src/main.rs +++ b/sysmaster/src/main.rs @@ -12,15 +12,15 @@ use daemonize::Daemonize; use libsysinspect::{ SysinspectError, cfg::{mmconf::MasterConfig, select_config_path}, - logger, + logger::{self, STDOUTLogger}, }; use log::LevelFilter; -use std::{env, fs::File}; +use std::{env, fs::File, sync::OnceLock}; use std::{path::PathBuf, process::exit}; static APPNAME: &str = "sysmaster"; static VERSION: &str = "0.4.0"; -static LOGGER: logger::STDOUTLogger = logger::STDOUTLogger; +static LOGGER: OnceLock = OnceLock::new(); fn start_master(cfg: MasterConfig) -> Result<(), SysinspectError> { tokio::runtime::Runtime::new()?.block_on(async { @@ -62,7 +62,7 @@ fn main() -> Result<(), SysinspectError> { } // Setup logger - if let Err(err) = log::set_logger(&LOGGER).map(|()| { + if let Err(err) = log::set_logger(LOGGER.get_or_init(|| STDOUTLogger::new(params.get_flag("no-color")))).map(|()| { log::set_max_level(match params.get_count("debug") { 0 => LevelFilter::Info, 1 => LevelFilter::Debug, diff --git a/sysminion/src/clidef.rs b/sysminion/src/clidef.rs index 0c164d19..60255958 100644 --- a/sysminion/src/clidef.rs +++ b/sysminion/src/clidef.rs @@ -98,6 +98,12 @@ pub fn cli(version: &'static str, appname: &'static str) -> Command { .action(ArgAction::Count) .help("Set debug mode for more verbose output. Increase this flag for more verbosity."), ) + .arg( + Arg::new("no-color") + .long("no-color") + .action(ArgAction::SetTrue) + .help("Disable colored output in logs"), + ) .arg( Arg::new("help") .short('h') diff --git a/sysminion/src/main.rs b/sysminion/src/main.rs index 6602c145..164f0120 100644 --- a/sysminion/src/main.rs +++ b/sysminion/src/main.rs @@ -15,14 +15,14 @@ use libsysinspect::{ logger, }; use log::LevelFilter; -use std::{env, fs::File, process::exit}; +use std::{env, fs::File, process::exit, sync::OnceLock}; use tokio::task::JoinHandle; use crate::minion::SysMinion; static APPNAME: &str = "sysminion"; static VERSION: &str = "0.4.0"; -static LOGGER: logger::STDOUTLogger = logger::STDOUTLogger; +static LOGGER: OnceLock = OnceLock::new(); fn start_minion(cfg: MinionConfig, fp: Option) -> Result<(), SysinspectError> { let runtime = tokio::runtime::Runtime::new().map_err(|e| SysinspectError::DynError(Box::new(e)))?; @@ -103,7 +103,7 @@ fn main() -> std::io::Result<()> { } // Setup logger - if let Err(err) = log::set_logger(&LOGGER).map(|()| { + if let Err(err) = log::set_logger(LOGGER.get_or_init(|| logger::STDOUTLogger::new(params.get_flag("no-color")))).map(|()| { log::set_max_level(match params.get_count("debug") { 0 => LevelFilter::Info, 1 => LevelFilter::Debug, From 156d163646f785022bc3635603e1894d9e797ee4 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 18 Dec 2025 13:29:24 +0100 Subject: [PATCH 24/44] Lintfix logger init --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 5331c6b8..10747393 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,7 @@ fn set_logger(p: &ArgMatches) { let log: &'static dyn log::Log = if *p.get_one::("ui").unwrap_or(&false) { &MEM_LOGGER as &'static dyn log::Log } else { - LOGGER.get_or_init(|| STDOUTLogger::default()) as &'static dyn log::Log + LOGGER.get_or_init(STDOUTLogger::default) as &'static dyn log::Log }; if let Err(err) = log::set_logger(log).map(|()| { From 9e35e94fe84cfbc0112ce3e72a13d1114e945fdb Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 18 Dec 2025 15:20:21 +0100 Subject: [PATCH 25/44] Make ID mandatory in the configuration of virtual minion --- libsysinspect/src/cfg/mmconf.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libsysinspect/src/cfg/mmconf.rs b/libsysinspect/src/cfg/mmconf.rs index 19567f0a..4f43e53b 100644 --- a/libsysinspect/src/cfg/mmconf.rs +++ b/libsysinspect/src/cfg/mmconf.rs @@ -566,7 +566,7 @@ impl MinionConfig { /// At least two nodes must be present (it is a cluster after all). #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct ClusteredMinion { - id: Option, + id: Value, hostname: Option, traits: Option>, nodes: Vec, @@ -575,7 +575,7 @@ pub struct ClusteredMinion { impl ClusteredMinion { /// Validate clustered minion definition pub fn is_valid(&self) -> bool { - (self.id.is_some() || self.hostname.is_some() || self.traits.is_some()) && self.nodes.len() >= 2 + (self.hostname.is_some() || self.traits.is_some()) && self.nodes.len() >= 2 } /// Get clustered minion nodes @@ -584,8 +584,11 @@ impl ClusteredMinion { } /// Get clustered minion id - pub fn id(&self) -> Option<&Value> { - self.id.as_ref() + pub fn id(&self) -> String { + match &self.id { + Value::String(s) => s.clone(), + _ => util::dataconv::to_string(Some(self.id.clone())).unwrap_or_default().trim().to_string(), + } } /// Get clustered minion hostname @@ -614,6 +617,7 @@ impl ClusteredMinion { pub struct ClusteredMinionScope { id: Option, query: Option, + hostname: Option, traits: Option>, } @@ -637,6 +641,11 @@ impl ClusteredMinionScope { pub fn traits(&self) -> Option<&IndexMap> { self.traits.as_ref() } + + /// Get clustered minion scope hostname + pub fn hostname(&self) -> Option<&String> { + self.hostname.as_ref() + } } #[derive(Debug, Serialize, Deserialize, Default, Clone)] From 0342f17f53cd5a4ebbf2026496fda85c35a7b29a Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 18 Dec 2025 15:20:55 +0100 Subject: [PATCH 26/44] Group minions by hostnames for a virtual minion --- sysmaster/src/cluster.rs | 103 ++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index 1b0ec027..da678bb0 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -1,20 +1,19 @@ // Cluster management for sysmaster +use crate::registry::mreg::MinionRegistry; use globset::Glob; use libsysinspect::{SysinspectError, cfg::mmconf::ClusteredMinion, proto::MasterMessage}; -use serde_yaml::Value; +use serde_json::Value; use std::{ collections::{HashMap, HashSet}, sync::Arc, + vec, }; use tokio::sync::Mutex; -use crate::registry::mreg::MinionRegistry; - #[derive(Debug, Clone, Default)] /// Representation of a clustered node pub struct ClusterNode { - hostname: String, mid: String, traits: HashMap, } @@ -22,16 +21,22 @@ pub struct ClusterNode { /// Cluster node representation impl ClusterNode { /// Create a new cluster node - pub fn new(hostname: &str, mid: &str, traits: HashMap) -> ClusterNode { - ClusterNode { hostname: hostname.to_string(), mid: mid.to_string(), traits } + pub fn new(mid: &str, traits: HashMap) -> ClusterNode { + ClusterNode { mid: mid.to_string(), traits } } /// Match hostname with glob pattern + /// It takes data from the traits map, trying to get first short name, then FQDN. fn match_hostname(&self, pattern: &str) -> bool { - match Glob::new(pattern) { - Ok(g) => g.compile_matcher().is_match(&self.hostname), - Err(_) => false, + for h in vec!["system.hostname", "system.hostname.fqdn"].iter() { + if let Some(Value::String(hn)) = self.traits.get(*h) { + return match Glob::new(pattern) { + Ok(g) => g.compile_matcher().is_match(hn), + Err(_) => false, + }; + } } + false } /// Match traits @@ -60,6 +65,7 @@ pub struct VirtualMinion { #[derive(Debug, Clone)] pub struct VirtualMinionsCluster { mreg: Arc>, + cfg: Vec, virtual_minions: Vec, // Configured clustered minions /* Here must be a sort of state of the cluster, e.g., which minion is @@ -69,45 +75,74 @@ pub struct VirtualMinionsCluster { impl VirtualMinionsCluster { pub fn new(cfg: Vec, mreg: Arc>) -> VirtualMinionsCluster { - if cfg.is_empty() { - return VirtualMinionsCluster { virtual_minions: Vec::new(), mreg }; + VirtualMinionsCluster { virtual_minions: Vec::new(), mreg, cfg } + } + + fn filter_traits(&self, traits: &HashMap) -> HashMap { + let mut filtered = HashMap::new(); + let tk = vec!["system.hostname", "system.hostname.fqdn", "system.hostname.ip"]; + for (k, v) in traits.iter() { + if tk.contains(&k.as_str()) { + filtered.insert(k.clone(), v.clone()); + } } - let mut v_minions: Vec = Vec::new(); + filtered + } + + pub async fn init(&mut self) -> Result<(), SysinspectError> { + let mut mreg = self.mreg.lock().await; // Call all that stuff in self.init() later to keep mreg async - for m in cfg.iter() { - let nodes: Vec = Vec::new(); - let id: String = m.id().and_then(|v| v.as_str()).unwrap_or_default().to_string(); - let traits: HashMap = match m.traits() { - Some(t) => t.clone().into_iter().collect(), + for m in self.cfg.iter() { + log::debug!("Processing clustered minion: {:#?}", m); + let mut nodes: Vec = Vec::new(); + let cm_traits: HashMap = match m.traits() { + Some(t) => t.clone().into_iter().map(|(k, v)| (k, serde_json::to_value(v).unwrap_or(Value::Null))).collect(), None => HashMap::new(), }; - for node in m.nodes().iter() { - let _node_traits: HashMap = match node.traits() { - Some(t) => t.clone().into_iter().collect(), + for node_scope in m.nodes().iter() { + // Selectors + let id_sel = node_scope.id().and_then(|v| v.as_str()).unwrap_or_default(); + let hn_sel = node_scope.hostname().and_then(|s| Some(s.as_str())).unwrap_or_default(); + let qr_sel = node_scope.query().as_deref().unwrap_or(&"".to_string()); + let tr_sel: HashMap = match node_scope.traits() { + Some(t) => t.clone().into_iter().map(|(k, v)| (k, serde_json::to_value(v).unwrap_or(Value::Null))).collect(), None => HashMap::new(), }; - // TODO: find here minion nodes first - // First, glob the database, get minions matching the hostname pattern and/or traits. - // Then, create ClusterNode instances for each matched minion. - //let node = ClusterNode::new(&node.hostname().cloned().unwrap_or_default(), &node.id().cloned().unwrap_or_default(), node_traits); - //nodes.push(node); + /* + Only hostname selector is implemented. + To add more: + 1. nodes must be a hashset to avoid duplicates + 2. implement id_sel, qr_sel, tr_sel handling here + */ + + // Get minion records matching the hostname or IP pattern + if !hn_sel.is_empty() { + for mm in mreg.get_by_hostname_or_ip(hn_sel)?.iter() { + log::debug!(" Matched minion for clustered node {}: {:?}", hn_sel, mm.id()); + nodes.push(ClusterNode::new(mm.id(), self.filter_traits(&mm.get_traits().clone()))); + } + } } - let v_minion = VirtualMinion { id, hostnames: vec![m.hostname().cloned().unwrap_or_default()], traits, minions: nodes }; - v_minions.push(v_minion); + if !nodes.is_empty() { + self.virtual_minions.push(VirtualMinion { + id: m.id(), + hostnames: vec![m.hostname().cloned().unwrap_or_default()], + traits: cm_traits, + minions: nodes, + }); + } } - VirtualMinionsCluster { virtual_minions: v_minions, mreg } - } - - pub async fn init(&self) -> Result<(), SysinspectError> { - let minions = self.mreg.lock().await.get_by_hostname_or_ip("*")?; - for mr in minions.iter() { - log::info!("Discovered minion in cluster init: {:?}", mr.id()); + if self.virtual_minions.is_empty() { + log::warn!("No clustered minions configured or found in the cluster."); + } else { + log::info!("Clustered minion details: {:#?}", self.virtual_minions); } + Ok(()) } From 0a0b3c3f35b9139697c40d5274982a39ca73bd06 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 18 Dec 2025 15:21:37 +0100 Subject: [PATCH 27/44] Current lintfixes --- sysmaster/src/cluster.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index da678bb0..e9f80b86 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -28,7 +28,7 @@ impl ClusterNode { /// Match hostname with glob pattern /// It takes data from the traits map, trying to get first short name, then FQDN. fn match_hostname(&self, pattern: &str) -> bool { - for h in vec!["system.hostname", "system.hostname.fqdn"].iter() { + for h in ["system.hostname", "system.hostname.fqdn"].iter() { if let Some(Value::String(hn)) = self.traits.get(*h) { return match Glob::new(pattern) { Ok(g) => g.compile_matcher().is_match(hn), @@ -80,7 +80,7 @@ impl VirtualMinionsCluster { fn filter_traits(&self, traits: &HashMap) -> HashMap { let mut filtered = HashMap::new(); - let tk = vec!["system.hostname", "system.hostname.fqdn", "system.hostname.ip"]; + let tk = ["system.hostname", "system.hostname.fqdn", "system.hostname.ip"]; for (k, v) in traits.iter() { if tk.contains(&k.as_str()) { filtered.insert(k.clone(), v.clone()); @@ -103,10 +103,10 @@ impl VirtualMinionsCluster { for node_scope in m.nodes().iter() { // Selectors - let id_sel = node_scope.id().and_then(|v| v.as_str()).unwrap_or_default(); - let hn_sel = node_scope.hostname().and_then(|s| Some(s.as_str())).unwrap_or_default(); - let qr_sel = node_scope.query().as_deref().unwrap_or(&"".to_string()); - let tr_sel: HashMap = match node_scope.traits() { + let _id_sel = node_scope.id().and_then(|v| v.as_str()).unwrap_or_default(); + let hn_sel = node_scope.hostname().map(|s| s.as_str()).unwrap_or_default(); + let _qr_sel = node_scope.query().unwrap_or(&"".to_string()); + let _tr_sel: HashMap = match node_scope.traits() { Some(t) => t.clone().into_iter().map(|(k, v)| (k, serde_json::to_value(v).unwrap_or(Value::Null))).collect(), None => HashMap::new(), }; From d393ec928368fc7e7cbd4937843de45907f4e7e6 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 18 Dec 2025 15:31:10 +0100 Subject: [PATCH 28/44] Change poor's man online monitor format. TODO: Remove all that and move to the UI --- sysmaster/src/master.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 155c6a06..93fe51eb 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -563,12 +563,12 @@ impl SysMaster { let ip = traits.get("system.hostname.ip").and_then(|v| v.as_str()).unwrap_or("unknown"); msg.push(format!( - "{}. {} ({}), ID: {} is {}", + "{}. {} {} - {} ({})", idx + 1, - h.bright_yellow(), - ip.yellow(), - mid.cyan(), - if alive { "βœ…" } else { "❌" } + if alive { " " } else { "!" }, + if alive { mid.cyan() } else { mid.white() }, + if alive { h.bright_green() } else { h.yellow() }, + if alive { ip.bright_green() } else { ip.yellow() }, )); } log::info!("Status of all registered minions:\n{}", msg.join("\n")); From ed99e7bdbf1b4831bd01ea2308d714f9cbc54628 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 19 Dec 2025 16:38:21 +0100 Subject: [PATCH 29/44] Pass session keeper to the cluster counter --- sysmaster/src/cluster.rs | 19 ++++++++++++++----- sysmaster/src/master.rs | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index e9f80b86..1032f18b 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -1,6 +1,6 @@ // Cluster management for sysmaster -use crate::registry::mreg::MinionRegistry; +use crate::registry::{mreg::MinionRegistry, session::SessionKeeper}; use globset::Glob; use libsysinspect::{SysinspectError, cfg::mmconf::ClusteredMinion, proto::MasterMessage}; use serde_json::Value; @@ -65,6 +65,7 @@ pub struct VirtualMinion { #[derive(Debug, Clone)] pub struct VirtualMinionsCluster { mreg: Arc>, + session: Arc>, cfg: Vec, virtual_minions: Vec, // Configured clustered minions /* @@ -74,8 +75,8 @@ pub struct VirtualMinionsCluster { } impl VirtualMinionsCluster { - pub fn new(cfg: Vec, mreg: Arc>) -> VirtualMinionsCluster { - VirtualMinionsCluster { virtual_minions: Vec::new(), mreg, cfg } + pub fn new(cfg: Vec, mreg: Arc>, session: Arc>) -> VirtualMinionsCluster { + VirtualMinionsCluster { virtual_minions: Vec::new(), mreg, cfg, session } } fn filter_traits(&self, traits: &HashMap) -> HashMap { @@ -159,6 +160,14 @@ impl VirtualMinionsCluster { Vec::new() } - /// Update cluster state by pong response from a minion - pub fn update_state(&mut self) {} + /// Call a query on the clustered minion. + /// This will do the following: + /// 1. Drop offline minions, if any (based on session state) + /// 2. Analyse each minion status/load + /// 3. Select the best-fit minion(s) to run the query + /// + /// This method must be called before master pings the minions with discovery + /// type ping (not general) and thus updates the session state with alive/dead minions + /// and their job load. + pub fn call(&mut self, query: &str) {} } diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 93fe51eb..da43952f 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -72,7 +72,7 @@ impl SysMaster { let mreg = Arc::new(Mutex::new(MinionRegistry::new(cfg.minion_registry_root())?)); let evtreg = Arc::new(Mutex::new(EventsRegistry::new(cfg.telemetry_location(), cfg.history())?)); let evtipc = Arc::new(DbIPCService::new(Arc::clone(&evtreg), cfg.telemetry_socket().to_str().unwrap_or_default())?); - let vmc = VirtualMinionsCluster::new(cfg.cluster().to_owned(), Arc::clone(&mreg)); + let vmc = VirtualMinionsCluster::new(cfg.cluster().to_owned(), Arc::clone(&mreg), Arc::clone(&SHARED_SESSION)); Ok(SysMaster { cfg, broadcast: tx, From e944c7b8112f3bf34934558299ebe2d8d7c401a3 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Mon, 12 Jan 2026 16:43:59 +0100 Subject: [PATCH 30/44] Resolve hostnames of the physical minions within virtual minion cluster from the query. Currently only '*' --- sysmaster/src/cluster.rs | 44 ++++++++++++++++++++++++++++++++++++- sysmaster/src/master.rs | 23 +++++++++++++++---- sysmaster/src/master_itf.rs | 2 +- sysminion/src/minion.rs | 2 +- 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index 1032f18b..8b2852b9 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -169,5 +169,47 @@ impl VirtualMinionsCluster { /// This method must be called before master pings the minions with discovery /// type ping (not general) and thus updates the session state with alive/dead minions /// and their job load. - pub fn call(&mut self, query: &str) {} + pub async fn get_hostnames(&mut self, query: &str) -> Vec { + let mut mids: Vec = Vec::new(); + let mut hostnames: Vec = Vec::new(); + + log::debug!("Getting hostnames for virtual minions cluster with query: {query}"); + + // Get all of them, if "*" is given + if query == "*" { + for vm in self.virtual_minions.iter() { + for physical_minion in vm.minions.iter() { + mids.push(physical_minion.mid.clone()); + } + } + } else { + log::error!("Only '*' query is supported for virtual minions cluster at the moment."); + } + + // Construct hostnames from the query list + for mid in mids.iter() { + let mrec = match self.mreg.lock().await.get(mid) { + Ok(mrec) => mrec, + Err(err) => { + log::error!("Unable to get minion record for {mid}: {err}"); + continue; + } + }; + if mrec.is_none() { + log::error!("Minion record for {mid} not found"); + continue; + } + let mrec = mrec.unwrap(); + if self.session.lock().await.alive(mrec.id()) { + let hn = mrec.get_traits().get("system.hostname.fqdn").and_then(|v| v.as_str()).unwrap_or_default(); + if !hn.is_empty() { + hostnames.push(hn.to_string()); + } + } else { + log::info!("Minion {} is offline, skipping", mid); + } + } + + hostnames + } } diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index da43952f..f5680e18 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -213,18 +213,31 @@ impl SysMaster { } /// Construct a Command message to the minion - pub(crate) fn msg_query(&mut self, payload: &str) -> Option { + pub(crate) async fn msg_query(&mut self, payload: &str) -> Option { let query = payload.split(";").map(|s| s.to_string()).collect::>(); if let [querypath, query, traits, mid, context] = query.as_slice() { + let is_virtual = query.to_lowercase().starts_with("v:"); + let query = query.to_lowercase().replace("v:", ""); + log::debug!("Context: {context}"); + let mut hostnames: Vec = query.split(',').map(|h| h.to_string()).collect(); + let mut tgt = MinionTarget::new(mid, ""); tgt.set_scheme(querypath); tgt.set_traits_query(traits); tgt.set_context_query(context); - for hostname in query.split(",") { + + if is_virtual { + hostnames = self.vmcluster.get_hostnames(&query).await; + log::debug!("Hostnames in the virtual minion cluster: {:#?}", hostnames); + } + + for hostname in hostnames.iter() { tgt.add_hostname(hostname); } + log::debug!("Target: {:#?}", tgt); + let mut out: IndexMap = IndexMap::default(); for em in self.cfg.fileserver_models() { for (n, cs) in scan_files_sha256(self.cfg.fileserver_mdl_root(false).join(em), Some(MODEL_FILE_EXT)) { @@ -249,6 +262,8 @@ impl SysMaster { msg.set_target(tgt); msg.set_retcode(ProtoErrorCode::Success); + log::debug!("Constructed message: {:#?}", msg); + return Some(msg); } @@ -604,7 +619,7 @@ impl SysMaster { log::debug!("Querying minions: {payload}"); let msg = { let mut guard = master.lock().await; - guard.msg_query(&payload) + guard.msg_query(&payload).await }; SysMaster::bcast_master_msg(&bcast, cfg.telemetry_enabled(), Arc::clone(&master), msg).await; } @@ -783,7 +798,7 @@ impl SysMaster { async move { let (bcast, msg, cfg) = { let mut master = master.lock().await; - (master.broadcast().clone(), master.msg_query(tdef.query().as_str()), master.cfg().clone()) + (master.broadcast().clone(), master.msg_query(tdef.query().as_str()).await, master.cfg().clone()) }; SysMaster::bcast_master_msg(&bcast, cfg.telemetry_enabled(), Arc::clone(&master), msg).await; } diff --git a/sysmaster/src/master_itf.rs b/sysmaster/src/master_itf.rs index 3f2cde29..f985af49 100644 --- a/sysmaster/src/master_itf.rs +++ b/sysmaster/src/master_itf.rs @@ -12,7 +12,7 @@ impl MasterInterface for SysMaster { /// Query operation async fn query(&mut self, query: String) -> Result<(), SysinspectError> { - if let Some(msg) = self.msg_query(&query) { + if let Some(msg) = self.msg_query(&query).await { if let Some(master) = self.as_ptr() { SysMaster::bcast_master_msg(&self.broadcast(), self.cfg_ref().telemetry_enabled(), master, Some(msg.clone())).await; } else { diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index 5a3ef1ff..844700c3 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -589,7 +589,7 @@ impl SysMinion { // Is matching this host? let mut skip = true; - let hostname = dataconv::as_str(traits.get("system.hostname")); + let hostname = dataconv::as_str(traits.get("system.hostname.fqdn")); // Fully qualified domain name or short, if your network is crap if !hostname.is_empty() { for hq in tgt.hostnames() { if let Ok(hq) = glob::Pattern::new(&hq) From ef6ac414bba7a2eb442a27a58be343ef578e5ee2 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Tue, 13 Jan 2026 17:18:01 +0100 Subject: [PATCH 31/44] Parse minion data (still needs to remove the dangling HashMap) --- libsysinspect/src/proto/mod.rs | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/libsysinspect/src/proto/mod.rs b/libsysinspect/src/proto/mod.rs index a8389e8d..7b07f483 100644 --- a/libsysinspect/src/proto/mod.rs +++ b/libsysinspect/src/proto/mod.rs @@ -82,6 +82,33 @@ impl MasterMessage { } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MinionMessageData { + eid: String, + aid: String, + sid: String, + cid: String, + timestamp: String, + response: MinionMessageResponse, + constraints: Value, + telemetry: Value, +} + +impl MinionMessageData { + /// Get cycle ID + pub fn cid(&self) -> &String { + &self.cid + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MinionMessageResponse { + retcode: usize, + warning: Option, + message: String, + data: Value, +} + /// Minion message #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MinionMessage { @@ -146,6 +173,28 @@ impl MinionMessage { pub fn payload(&self) -> &Value { &self.data } + + /// Get data as MinionMessageData + pub fn get_data(&self) -> MinionMessageData { + match serde_json::from_value::(self.data.clone()) { + Ok(data) => data, + Err(_) => MinionMessageData { + eid: "".to_string(), + aid: "".to_string(), + sid: "".to_string(), + cid: "".to_string(), + timestamp: "".to_string(), + response: MinionMessageResponse { + retcode: ProtoErrorCode::GeneralFailure as usize, + warning: None, + message: "Unable to parse MinionMessageData structure".to_string(), + data: Value::Null, + }, + constraints: Value::Null, + telemetry: Value::Null, + }, + } + } } /// Minion target From 912c04f958528a5b9c8308023dc93b82d35d44cd Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Tue, 13 Jan 2026 17:18:27 +0100 Subject: [PATCH 32/44] Decide on less used minion (meta-based) --- sysmaster/src/cluster.rs | 109 ++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 19 deletions(-) diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index 8b2852b9..effd089d 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -1,6 +1,6 @@ // Cluster management for sysmaster -use crate::registry::{mreg::MinionRegistry, session::SessionKeeper}; +use crate::registry::{mreg::MinionRegistry, session::SessionKeeper, taskreg::TaskRegistry}; use globset::Glob; use libsysinspect::{SysinspectError, cfg::mmconf::ClusteredMinion, proto::MasterMessage}; use serde_json::Value; @@ -61,12 +61,30 @@ pub struct VirtualMinion { traits: HashMap, minions: Vec, // Configured physical minions } +impl VirtualMinion { + /// Match hostname with glob pattern + /// It checks all configured hostnames for the virtual minion. + fn match_hostname(&self, query: &str) -> bool { + for hn in self.hostnames.iter() { + match Glob::new(query) { + Ok(g) => { + if g.compile_matcher().is_match(hn) { + return true; + } + } + Err(_) => continue, + } + } + false + } +} #[derive(Debug, Clone)] pub struct VirtualMinionsCluster { mreg: Arc>, session: Arc>, cfg: Vec, + task_tracker: Arc>, virtual_minions: Vec, // Configured clustered minions /* Here must be a sort of state of the cluster, e.g., which minion is @@ -75,8 +93,10 @@ pub struct VirtualMinionsCluster { } impl VirtualMinionsCluster { - pub fn new(cfg: Vec, mreg: Arc>, session: Arc>) -> VirtualMinionsCluster { - VirtualMinionsCluster { virtual_minions: Vec::new(), mreg, cfg, session } + pub fn new( + cfg: Vec, mreg: Arc>, session: Arc>, task_tracker: Arc>, + ) -> VirtualMinionsCluster { + VirtualMinionsCluster { virtual_minions: Vec::new(), mreg, cfg, session, task_tracker } } fn filter_traits(&self, traits: &HashMap) -> HashMap { @@ -160,34 +180,85 @@ impl VirtualMinionsCluster { Vec::new() } - /// Call a query on the clustered minion. - /// This will do the following: - /// 1. Drop offline minions, if any (based on session state) - /// 2. Analyse each minion status/load - /// 3. Select the best-fit minion(s) to run the query - /// - /// This method must be called before master pings the minions with discovery - /// type ping (not general) and thus updates the session state with alive/dead minions - /// and their job load. - pub async fn get_hostnames(&mut self, query: &str) -> Vec { + /// Get all minion IDs matching the query + fn query_mids(&self, query: &str) -> Vec { let mut mids: Vec = Vec::new(); - let mut hostnames: Vec = Vec::new(); - log::debug!("Getting hostnames for virtual minions cluster with query: {query}"); // Get all of them, if "*" is given if query == "*" { for vm in self.virtual_minions.iter() { - for physical_minion in vm.minions.iter() { - mids.push(physical_minion.mid.clone()); + for m in vm.minions.iter() { + mids.push(m.mid.clone()); } } } else { - log::error!("Only '*' query is supported for virtual minions cluster at the moment."); + for vm in self.virtual_minions.iter() { + if vm.match_hostname(query) { + log::debug!("Virtual minion {} matched hostname query {}", vm.id, query); + for physical_minion in vm.minions.iter() { + mids.push(physical_minion.mid.clone()); + } + } + } } + mids + } + + /// Decide the best-fit minion for the given query. + pub async fn decide(&self, query: &str) -> Option { + let mids = self.query_mids(query); + let mut fmid: Option = None; + let mut fmidt: usize = usize::MAX; - // Construct hostnames from the query list for mid in mids.iter() { + // Skip offline minions + if !self.session.lock().await.alive(mid) { + continue; + } + + let t = self.task_tracker.lock().await.minion_tasks(mid).len(); + if t == 0 { + fmid = Some(mid.to_string()); + break; + } + + if t < fmidt { + fmidt = t; + fmid = Some(mid.to_string()); + } + } + + if let Some(fmid) = fmid { + let r = match self.mreg.lock().await.get(&fmid) { + Ok(mrec) => mrec, + Err(err) => { + log::error!("Unable to get minion record for {fmid}: {err}"); + return None; + } + }; + if let Some(r) = r { + return r.get_traits().get("system.hostname.fqdn").and_then(|v| v.as_str()).map(|s| s.to_string()); + } + } + + None + } + + /// Call a query on the clustered minion. + /// This will do the following: + /// 1. Drop offline minions, if any (based on session state) + /// 2. Analyse each minion status/load + /// 3. Select the best-fit minion(s) to run the query + /// + /// This method must be called before master pings the minions with discovery + /// type ping (not general) and thus updates the session state with alive/dead minions + /// and their job load. + pub async fn get_hostnames(&self, query: &str) -> Vec { + let mut hostnames: Vec = Vec::new(); + + // Construct hostnames from the query list + for mid in self.query_mids(query).iter() { let mrec = match self.mreg.lock().await.get(mid) { Ok(mrec) => mrec, Err(err) => { From 5e995367e3dccc776abbe123fa80d1cd933e1769 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Tue, 13 Jan 2026 17:19:09 +0100 Subject: [PATCH 33/44] Track tasks on the master side (initial!) --- sysmaster/src/master.rs | 43 +++++++++++++++++++----- sysmaster/src/master_itf.rs | 8 ++++- sysmaster/src/registry/mod.rs | 1 + sysmaster/src/registry/mreg.rs | 39 ++++++++++++++++++++-- sysmaster/src/registry/taskreg.rs | 54 +++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 sysmaster/src/registry/taskreg.rs diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index f5680e18..39cb8b57 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -5,6 +5,7 @@ use crate::{ mkb::MinionsKeyRegistry, mreg::MinionRegistry, session::{self, SessionKeeper}, + taskreg::TaskRegistry, }, telemetry::{otel::OtelLogger, rds::FunctionReducer}, }; @@ -49,7 +50,7 @@ use tokio::{ }; // Session singleton -static SHARED_SESSION: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(SessionKeeper::new(30)))); +pub static SHARED_SESSION: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(SessionKeeper::new(30)))); static MODEL_CACHE: Lazy>>> = Lazy::new(|| Arc::new(Mutex::new(HashMap::new()))); #[derive(Debug)] @@ -58,6 +59,7 @@ pub struct SysMaster { broadcast: broadcast::Sender>, mkr: MinionsKeyRegistry, mreg: Arc>, + taskreg: Arc>, evtipc: Arc, to_drop: HashSet, session: Arc>, @@ -70,9 +72,10 @@ impl SysMaster { let (tx, _) = broadcast::channel::>(100); let mkr = MinionsKeyRegistry::new(cfg.minion_keys_root())?; let mreg = Arc::new(Mutex::new(MinionRegistry::new(cfg.minion_registry_root())?)); + let taskreg = Arc::new(Mutex::new(TaskRegistry::new())); let evtreg = Arc::new(Mutex::new(EventsRegistry::new(cfg.telemetry_location(), cfg.history())?)); let evtipc = Arc::new(DbIPCService::new(Arc::clone(&evtreg), cfg.telemetry_socket().to_str().unwrap_or_default())?); - let vmc = VirtualMinionsCluster::new(cfg.cluster().to_owned(), Arc::clone(&mreg), Arc::clone(&SHARED_SESSION)); + let vmcluster = VirtualMinionsCluster::new(cfg.cluster().to_owned(), Arc::clone(&mreg), Arc::clone(&SHARED_SESSION), Arc::clone(&taskreg)); Ok(SysMaster { cfg, broadcast: tx, @@ -81,8 +84,9 @@ impl SysMaster { session: Arc::clone(&SHARED_SESSION), mreg, evtipc, + taskreg, ptr: None, - vmcluster: vmc, + vmcluster, }) } @@ -220,16 +224,16 @@ impl SysMaster { let query = query.to_lowercase().replace("v:", ""); log::debug!("Context: {context}"); - let mut hostnames: Vec = query.split(',').map(|h| h.to_string()).collect(); + let hostnames: Vec = query.split(',').map(|h| h.to_string()).collect(); let mut tgt = MinionTarget::new(mid, ""); tgt.set_scheme(querypath); tgt.set_traits_query(traits); tgt.set_context_query(context); - if is_virtual { - hostnames = self.vmcluster.get_hostnames(&query).await; - log::debug!("Hostnames in the virtual minion cluster: {:#?}", hostnames); + if is_virtual && let Some(decided) = self.vmcluster.decide(&query).await { + log::warn!(">>> Decided to run on: {}", decided); + tgt.add_hostname(&decided); } for hostname in hostnames.iter() { @@ -315,10 +319,21 @@ impl SysMaster { m } + /// Get session keeper pub fn get_session(&self) -> Arc> { Arc::clone(&self.session) } + /// Get minion registry + pub fn get_minion_registry(&self) -> Arc> { + Arc::clone(&self.mreg) + } + + /// Get task registry + pub fn get_task_registry(&self) -> Arc> { + Arc::clone(&self.taskreg) + } + /// Process incoming minion messages #[allow(clippy::while_let_loop)] pub async fn do_incoming(master: Arc>, mut rx: tokio::sync::mpsc::Receiver<(Vec, String)>) { @@ -426,11 +441,16 @@ impl SysMaster { RequestType::Event => { log::debug!("Event for {}: {}", req.id(), req.payload()); + let d = req.get_data(); let c_master = Arc::clone(&master); tokio::spawn(async move { let m = c_master.lock().await; + m.taskreg.lock().await.deregister(d.cid(), req.id()); let mrec = m.mreg.lock().await.get(req.id()).unwrap_or_default().unwrap_or_default(); + // XXX: Fix this nonsense + // This should use get_data() method to extract payload properly + // Also replace HashMap in evtipc.add_event with it. let pl = match serde_json::from_str::>(req.payload().to_string().as_str()) { Ok(pl) => pl, Err(err) => { @@ -528,6 +548,8 @@ impl SysMaster { log::error!("Minion sends unknown request type"); } } + } else { + log::error!("Unable to parse minion message"); } } else { break; @@ -621,7 +643,12 @@ impl SysMaster { let mut guard = master.lock().await; guard.msg_query(&payload).await }; - SysMaster::bcast_master_msg(&bcast, cfg.telemetry_enabled(), Arc::clone(&master), msg).await; + SysMaster::bcast_master_msg(&bcast, cfg.telemetry_enabled(), Arc::clone(&master), msg.clone()).await; + { + let guard = master.lock().await; + let ids = guard.mreg.lock().await.get_targeted_minions(msg.as_ref().unwrap().target(), false).await; + guard.taskreg.lock().await.register(msg.as_ref().unwrap().cycle(), ids); + } } Ok(None) => break, // End of file, re-open the FIFO Err(e) => { diff --git a/sysmaster/src/master_itf.rs b/sysmaster/src/master_itf.rs index f985af49..ac89fabe 100644 --- a/sysmaster/src/master_itf.rs +++ b/sysmaster/src/master_itf.rs @@ -14,7 +14,13 @@ impl MasterInterface for SysMaster { async fn query(&mut self, query: String) -> Result<(), SysinspectError> { if let Some(msg) = self.msg_query(&query).await { if let Some(master) = self.as_ptr() { - SysMaster::bcast_master_msg(&self.broadcast(), self.cfg_ref().telemetry_enabled(), master, Some(msg.clone())).await; + // `bcast_master_msg` takes ownership of the master handle; keep a copy for later use. + SysMaster::bcast_master_msg(&self.broadcast(), self.cfg_ref().telemetry_enabled(), master.clone(), Some(msg.clone())).await; + { + let master_guard = master.lock().await; + let ids = master_guard.get_minion_registry().lock().await.get_targeted_minions(msg.target(), false).await; + log::error!(">>>>>>>>>>>>>>> Targeted minions: {:#?}", ids); + } } else { return Err(SysinspectError::InvalidQuery("Master pointer is not set".to_string())); } diff --git a/sysmaster/src/registry/mod.rs b/sysmaster/src/registry/mod.rs index 34ecf763..b7f31a66 100644 --- a/sysmaster/src/registry/mod.rs +++ b/sysmaster/src/registry/mod.rs @@ -6,3 +6,4 @@ pub mod mkb; pub mod mreg; pub mod rec; pub mod session; +pub mod taskreg; diff --git a/sysmaster/src/registry/mreg.rs b/sysmaster/src/registry/mreg.rs index 519c69bd..f251c3cb 100644 --- a/sysmaster/src/registry/mreg.rs +++ b/sysmaster/src/registry/mreg.rs @@ -1,9 +1,11 @@ +use crate::master::SHARED_SESSION; + use super::rec::MinionRecord; use globset::Glob; -use libsysinspect::SysinspectError; +use libsysinspect::{SysinspectError, proto::MinionTarget}; use serde_json::{Value, json}; use sled::{Db, Tree}; -use std::{collections::HashMap, fs, path::PathBuf}; +use std::{collections::HashMap, fs, path::PathBuf, sync::Arc}; const DB_MINIONS: &str = "minions"; @@ -193,4 +195,37 @@ impl MinionRegistry { Ok(mns) } + + /// Get targeted minion IDs from a MinionTarget + /// If `all` is true, return all matching minions regardless of their session status. + pub async fn get_targeted_minions(&mut self, target: &MinionTarget, all: bool) -> Vec { + // Direct ID specified + let session = Arc::clone(&SHARED_SESSION); + + if !target.id().is_empty() { + return vec![target.id().to_string()]; + } + + // Hostnames are specified + if !target.hostnames().is_empty() { + let mut ids: Vec = Vec::new(); + for hn in target.hostnames() { + match self.get_by_hostname_or_ip(&hn) { + Ok(mrecs) => { + for mrec in mrecs { + if all || session.lock().await.alive(mrec.id()) { + ids.push(mrec.id().to_string()); + } + } + } + Err(err) => { + log::error!("Unable to get minion by hostname {}: {}", hn, err); + } + } + } + return ids; + } + + vec![] + } } diff --git a/sysmaster/src/registry/taskreg.rs b/sysmaster/src/registry/taskreg.rs new file mode 100644 index 00000000..7121be89 --- /dev/null +++ b/sysmaster/src/registry/taskreg.rs @@ -0,0 +1,54 @@ +use std::collections::{HashMap, HashSet}; + +/* +Task Registry Module. + +This module defines the TaskRegistry struct, which is responsible for managing +the registration and tracking of tasks within the sysmaster system. + +Task Registry keeps track of active tasks, their states, and other relevant metadata. +It works like a simple key-value store where task IDs (Cycle ID or CID) are keys to array +of targeted minion IDs. A task considered done if it has no more "dangling" minion IDs. + +Task Registry has two modes: +1. In-memory database, tracking current online minions only. +2. Persistent database for resuming tasks for offline minions after sysmaster restarts. + */ +#[derive(Debug)] +pub struct TaskRegistry { + ongoing: HashMap>, // Map of task IDs to list of targeted minion IDs +} +impl TaskRegistry { + pub fn new() -> Self { + TaskRegistry { ongoing: HashMap::new() } + } + + /// Register a new task with its targeted minion IDs, incrmenting + pub fn register(&mut self, cid: &str, mids: Vec) { + self.ongoing.insert(cid.to_string(), mids.into_iter().collect()); + log::info!(">>> Registered task {cid} with targeted minions: {:#?}", self.ongoing.get(cid)); + } + + /// Deregister a minion ID from a task + pub fn deregister(&mut self, cid: &str, mid: &str) { + if let Some(minions) = self.ongoing.get_mut(cid) { + minions.remove(mid); + log::info!(">>> Deregistered minion {mid} from task {cid}. Remaining minions: {:#?}", minions); + if minions.is_empty() { + self.ongoing.remove(cid); + log::info!(">>> Task {cid} completed and removed from registry"); + } + } + } + + /// Get list of tasks a minion is involved in + pub fn minion_tasks(&self, mid: &str) -> Vec { + let mut tasks = Vec::new(); + for (cid, mids) in self.ongoing.iter() { + if mids.contains(mid) { + tasks.push(cid.clone()); + } + } + tasks + } +} From 500ce53325b9145b1ee10c52465042a79e97e72b Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 14 Jan 2026 22:11:37 +0100 Subject: [PATCH 34/44] Add strum dep --- Cargo.lock | 2 ++ libsysinspect/Cargo.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index fad33906..dd6a7dc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3076,6 +3076,8 @@ dependencies = [ "sha2", "shlex", "sled", + "strum 0.27.2", + "strum_macros 0.27.2", "sysinfo 0.34.2", "tera", "textwrap", diff --git a/libsysinspect/Cargo.toml b/libsysinspect/Cargo.toml index 797e562d..7ead47ea 100644 --- a/libsysinspect/Cargo.toml +++ b/libsysinspect/Cargo.toml @@ -54,3 +54,5 @@ humantime = "2.3.0" humantime-serde = "1.1.1" libc = "0.2.178" console = "0.16.2" +strum = "0.27.2" +strum_macros = "0.27.2" From a1da86718fdbea32592b19ee3defad4be5728da5 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 14 Jan 2026 22:12:19 +0100 Subject: [PATCH 35/44] Define protocol keys --- libsysinspect/src/proto/rqtypes.rs | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/libsysinspect/src/proto/rqtypes.rs b/libsysinspect/src/proto/rqtypes.rs index c79458be..0ceeceb9 100644 --- a/libsysinspect/src/proto/rqtypes.rs +++ b/libsysinspect/src/proto/rqtypes.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use strum_macros::Display; #[derive(Serialize, Deserialize, Debug, Clone)] pub enum ProtoValue { @@ -9,6 +10,59 @@ pub enum ProtoValue { PingTypeDiscovery, } +#[derive(Serialize, Deserialize, Debug, Clone, Display)] +pub enum ProtoKey { + /// Session Id + #[strum(serialize = "sid")] + #[serde(rename = "sid")] + SessionId, + + /// Cycle Id + #[strum(serialize = "cid")] + #[serde(rename = "cid")] + CycleId, + + /// Action Id + #[strum(serialize = "aid")] + #[serde(rename = "aid")] + ActionId, + + /// Entity Id + #[strum(serialize = "eid")] + #[serde(rename = "eid")] + EntityId, + + /// Protocol Type + #[strum(serialize = "pt")] + #[serde(rename = "pt")] + ProtoType, + + /// Payload + #[strum(serialize = "pl")] + #[serde(rename = "pl")] + Payload, + + /// Constraints + #[strum(serialize = "constraints")] + #[serde(rename = "constraints")] + Constraints, + + /// Response + #[strum(serialize = "response")] + #[serde(rename = "response")] + Response, + + /// Timestamp + #[strum(serialize = "timestamp")] + #[serde(rename = "timestamp")] + Timestamp, + + /// Telemetry + #[strum(serialize = "telemetry")] + #[serde(rename = "telemetry")] + Telemetry, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub enum RequestType { /// Minion registration request or context. From 311f6837a1ba5199bc80552a91e37006064e30bb Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 14 Jan 2026 22:13:56 +0100 Subject: [PATCH 36/44] Replace hardcoded keys with the defined enum --- libeventreg/src/kvdb.rs | 25 +++++++++++++------------ sysmaster/src/master.rs | 12 ++++++++---- sysmaster/src/telemetry/otel.rs | 9 +++++---- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/libeventreg/src/kvdb.rs b/libeventreg/src/kvdb.rs index c628f13d..f712c53e 100644 --- a/libeventreg/src/kvdb.rs +++ b/libeventreg/src/kvdb.rs @@ -5,6 +5,7 @@ use indexmap::IndexMap; use libsysinspect::{ SysinspectError, cfg::mmconf::HistoryConfig, + proto::rqtypes::ProtoKey, util::{self}, }; use serde::{Deserialize, Serialize}; @@ -25,15 +26,15 @@ impl EventData { } pub fn get_entity_id(&self) -> String { - util::dataconv::as_str(self.data.get("eid").cloned()) + util::dataconv::as_str(self.data.get(&ProtoKey::EntityId.to_string()).cloned()) } pub fn get_action_id(&self) -> String { - util::dataconv::as_str(self.data.get("aid").cloned()) + util::dataconv::as_str(self.data.get(&ProtoKey::ActionId.to_string()).cloned()) } pub fn get_status_id(&self) -> String { - util::dataconv::as_str(self.data.get("sid").cloned()) + util::dataconv::as_str(self.data.get(&ProtoKey::SessionId.to_string()).cloned()) } pub fn get_event_id(&self) -> String { @@ -41,21 +42,21 @@ impl EventData { } pub fn get_cycle_id(&self) -> String { - util::dataconv::as_str(self.data.get("cid").cloned()) + util::dataconv::as_str(self.data.get(&ProtoKey::CycleId.to_string()).cloned()) } pub fn get_constraints(&self) -> HashMap { - serde_json::from_value(self.data.get("constraints").unwrap().clone()).unwrap() + serde_json::from_value(self.data.get(&ProtoKey::Constraints.to_string()).unwrap().clone()).unwrap() } pub fn get_response(&self) -> HashMap { // Should work... :-) - serde_json::from_value(self.data.get("response").unwrap().clone()).unwrap() + serde_json::from_value(self.data.get(&ProtoKey::Response.to_string()).unwrap().clone()).unwrap() } pub fn get_response_mut(&mut self) -> Result<&mut serde_json::Map, String> { self.data - .get_mut("response") + .get_mut(&ProtoKey::Response.to_string()) .ok_or_else(|| "Key 'response' not found in data".to_string())? .as_object_mut() .ok_or_else(|| "Value for 'response' is not an object".to_string()) @@ -63,7 +64,7 @@ impl EventData { /// Get the timestamp pub fn get_timestamp(&self) -> String { - util::dataconv::as_str(self.data.get("timestamp").cloned()) + util::dataconv::as_str(self.data.get(&ProtoKey::Timestamp.to_string()).cloned()) } pub fn from_bytes(b: Vec) -> Result { @@ -76,7 +77,7 @@ impl EventData { /// Flattens the entire data into IndexMap pub fn flatten(&self) -> IndexMap { let mut out = IndexMap::new(); - Self::_flatten(self.data.get("response").unwrap(), "", &mut out); + Self::_flatten(self.data.get(&ProtoKey::Response.to_string()).unwrap(), "", &mut out); out } @@ -282,9 +283,9 @@ impl EventsRegistry { if let Err(err) = events.insert( format!( "{}/{}/{}", - util::dataconv::as_str(payload.get("eid").cloned()), - util::dataconv::as_str(payload.get("sid").cloned()), - util::dataconv::as_str(payload.get("aid").cloned()) + util::dataconv::as_str(payload.get(&ProtoKey::EntityId.to_string()).cloned()), + util::dataconv::as_str(payload.get(&ProtoKey::SessionId.to_string()).cloned()), + util::dataconv::as_str(payload.get(&ProtoKey::ActionId.to_string()).cloned()) ), serde_json::to_string(&payload)?.as_bytes(), ) { diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 39cb8b57..dd0a22d2 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -467,9 +467,9 @@ impl SysMaster { let sid = match m .evtipc .open_session( - util::dataconv::as_str(pl.get("eid").cloned()), - util::dataconv::as_str(pl.get("cid").cloned()), - util::dataconv::as_str(pl.get("timestamp").cloned()), + util::dataconv::as_str(pl.get(&ProtoKey::EntityId.to_string()).cloned()), + util::dataconv::as_str(pl.get(&ProtoKey::CycleId.to_string()).cloned()), + util::dataconv::as_str(pl.get(&ProtoKey::Timestamp.to_string()).cloned()), ) .await { @@ -512,7 +512,11 @@ impl SysMaster { return; } }; - let sid = match master.evtipc.get_session(&util::dataconv::as_str(pl.get("cid").cloned())).await { + let sid = match master + .evtipc + .get_session(&util::dataconv::as_str(pl.get(&ProtoKey::CycleId.to_string()).cloned())) + .await + { Ok(sid) => sid, Err(err) => { log::debug!("Unable to acquire session for this iteration: {err}"); diff --git a/sysmaster/src/telemetry/otel.rs b/sysmaster/src/telemetry/otel.rs index b01d1133..6521c3aa 100644 --- a/sysmaster/src/telemetry/otel.rs +++ b/sysmaster/src/telemetry/otel.rs @@ -4,6 +4,7 @@ use libeventreg::kvdb::EventData; use libsysinspect::{ SysinspectError, mdescr::telemetry::{DataExportType, EventSelector, StaticDataDestination}, + proto::rqtypes::ProtoKey, }; use libtelemetry::{ otel_log_json, @@ -57,7 +58,7 @@ impl OtelLogger { } fn get_selectors(pl: &HashMap) -> Vec { - match pl.get("telemetry").cloned() { + match pl.get(&ProtoKey::Telemetry.to_string()).cloned() { Some(value) => match serde_json::from_value::>(value) { Ok(selectors) => selectors, Err(err) => { @@ -73,7 +74,7 @@ impl OtelLogger { } fn get_response_data(&self, es: &EventSelector, pl: &HashMap) -> IndexMap { - let response = match pl.get("response").cloned() { + let response = match pl.get(&ProtoKey::Response.to_string()).cloned() { Some(resp) => resp, None => { log::error!("No response found"); @@ -186,13 +187,13 @@ impl OtelLogger { continue; } - let entity_id = self.payload.get("eid").and_then(|v| v.as_str()).unwrap_or_default().to_string(); + let entity_id = self.payload.get(&ProtoKey::EntityId.to_string()).and_then(|v| v.as_str()).unwrap_or_default().to_string(); if !entity_id.is_empty() && !es.filter().entity().is_empty() && !es.filter().entity().eq(&entity_id) { log::debug!("Event ID {} does not match entity filter: {}", entity_id, es.filter().entity()); continue; } - let action_id = self.payload.get("aid").and_then(|v| v.as_str()).unwrap_or_default().to_string(); + let action_id = self.payload.get(&ProtoKey::ActionId.to_string()).and_then(|v| v.as_str()).unwrap_or_default().to_string(); if !action_id.is_empty() && !es.filter().actions().is_empty() && !es.filter().actions().iter().any(|a| a.eq(&action_id)) { log::debug!("Action ID {} does not match filter: {}", action_id, es.filter().actions().join(", ")); continue; From 3668cec6de39bdc3118bf43d68319ec647990ae4 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 14 Jan 2026 22:14:12 +0100 Subject: [PATCH 37/44] Define ping payload data structure --- libsysinspect/src/proto/payload.rs | 56 +++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/libsysinspect/src/proto/payload.rs b/libsysinspect/src/proto/payload.rs index 2a741eef..2f7085d9 100644 --- a/libsysinspect/src/proto/payload.rs +++ b/libsysinspect/src/proto/payload.rs @@ -6,7 +6,7 @@ Payload types and their deserialisation. use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use serde_json::{from_value, Value}; +use serde_json::{Value, from_value}; /// Payload types pub enum PayloadType { @@ -85,3 +85,57 @@ impl ModStatePayload { &self.models_root } } + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct PingData { + #[serde(rename = "sid")] + sid: String, + + #[serde(rename = "pl")] + payload: PingPayload, + + #[serde(rename = "pt")] + ping_type: String, +} + +impl PingData { + pub fn from_value(v: Value) -> Result { + serde_json::from_value::(v) + } + + /// Get session id + pub fn sid(&self) -> &str { + &self.sid + } + + /// Get payload + pub fn payload(&self) -> &PingPayload { + &self.payload + } + + /// Get ping type + pub fn ping_type(&self) -> &str { + &self.ping_type + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct PingPayload { + #[serde(rename = "ld")] + load_average: f32, // load average + + #[serde(rename = "cd")] + completed: Vec, // completed task ids +} + +impl PingPayload { + /// Get load average + pub fn load_average(&self) -> f32 { + self.load_average + } + + /// Get completed task ids + pub fn completed(&self) -> &Vec { + &self.completed + } +} From 2fb6db4b3dddd4d42370eea25268dcd1db162ec4 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 14 Jan 2026 22:14:34 +0100 Subject: [PATCH 38/44] Count/flush tasks on heartbit --- sysmaster/src/master.rs | 22 ++++++---- sysmaster/src/registry/taskreg.rs | 67 +++++++++++++++++++++++++++---- sysminion/src/minion.rs | 25 ++++++++++-- sysminion/src/proto.rs | 20 +++++++-- sysminion/src/ptcounter.rs | 39 +++++++++++++----- 5 files changed, 138 insertions(+), 35 deletions(-) diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index dd0a22d2..1e24487b 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -22,12 +22,12 @@ use libsysinspect::{ proto::{ self, MasterMessage, MinionMessage, MinionTarget, ProtoConversion, errcodes::ProtoErrorCode, - payload::ModStatePayload, + payload::{ModStatePayload, PingData}, query::{ SCHEME_COMMAND, commands::{CLUSTER_ONLINE_MINIONS, CLUSTER_REMOVE_MINION}, }, - rqtypes::{ProtoValue, RequestType}, + rqtypes::{ProtoKey, ProtoValue, RequestType}, }, util::{self, iofs::scan_files_sha256}, }; @@ -401,18 +401,24 @@ impl SysMaster { let c_master = Arc::clone(&master); let c_id = req.id().to_string(); tokio::spawn(async move { + log::info!("Received pong from {:#?}", req.payload()); let guard = c_master.lock().await; - - let payload: HashMap = match serde_json::from_value(req.payload().clone()) { - Ok(data) => data, + let pm = match PingData::from_value(req.payload().clone()) { + Ok(pm) => pm, Err(err) => { - log::error!("Unable to parse pong payload: {err}"); + log::error!("Unable to parse pong message: {err}"); return; } }; - guard.get_session().lock().await.ping(&c_id, payload.get("sid").map(|s| s.as_str())); + guard.get_session().lock().await.ping(&c_id, Some(&pm.sid())); + let uptime = guard.get_session().lock().await.uptime(req.id()).unwrap_or_default(); - log::trace!("Update last contacted for {} (alive for {:.2} min)", req.id(), uptime as f64 / 60.0); + log::info!("Update last contacted for {} (alive for {:.2} min)", req.id(), uptime as f64 / 60.0); + + // Update task tracker + let taskreg = guard.get_task_registry(); + let mut taskreg = taskreg.lock().await; + taskreg.flush(&c_id, pm.payload().completed()); }); } diff --git a/sysmaster/src/registry/taskreg.rs b/sysmaster/src/registry/taskreg.rs index 7121be89..0f9d4e2c 100644 --- a/sysmaster/src/registry/taskreg.rs +++ b/sysmaster/src/registry/taskreg.rs @@ -1,4 +1,7 @@ -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + sync::Mutex, +}; /* Task Registry Module. @@ -16,35 +19,83 @@ Task Registry has two modes: */ #[derive(Debug)] pub struct TaskRegistry { - ongoing: HashMap>, // Map of task IDs to list of targeted minion IDs + ongoing: Mutex>>, // Map of task IDs to list of targeted minion IDs } impl TaskRegistry { pub fn new() -> Self { - TaskRegistry { ongoing: HashMap::new() } + TaskRegistry { ongoing: Mutex::new(HashMap::new()) } } /// Register a new task with its targeted minion IDs, incrmenting pub fn register(&mut self, cid: &str, mids: Vec) { - self.ongoing.insert(cid.to_string(), mids.into_iter().collect()); - log::info!(">>> Registered task {cid} with targeted minions: {:#?}", self.ongoing.get(cid)); + let mut ongoing = match self.ongoing.lock() { + Ok(guard) => guard, + Err(e) => { + log::error!("Failed to acquire lock for task registry: {}", e); + return; + } + }; + + ongoing.insert(cid.to_string(), mids.into_iter().collect()); + log::info!(">>> Registered task {cid} with targeted minions: {:#?}", ongoing.get(cid)); } /// Deregister a minion ID from a task pub fn deregister(&mut self, cid: &str, mid: &str) { - if let Some(minions) = self.ongoing.get_mut(cid) { + let mut ongoing = match self.ongoing.lock() { + Ok(guard) => guard, + Err(e) => { + log::error!("Failed to acquire lock for task registry: {}", e); + return; + } + }; + if let Some(minions) = ongoing.get_mut(cid) { minions.remove(mid); log::info!(">>> Deregistered minion {mid} from task {cid}. Remaining minions: {:#?}", minions); if minions.is_empty() { - self.ongoing.remove(cid); + ongoing.remove(cid); log::info!(">>> Task {cid} completed and removed from registry"); } } } + pub fn flush(&mut self, mid: &str, cids: &Vec) { + for cid in cids { + log::info!(">>> Flushing minion {mid} from task {cid}"); + } + + let mut ongoing = match self.ongoing.lock() { + Ok(guard) => guard, + Err(e) => { + log::error!("Failed to acquire lock for task registry: {}", e); + return; + } + }; + ongoing.retain(|cid, mids| { + if mids.contains(mid) { + log::info!(">>> Flushing minion {mid} from task {cid}"); + mids.remove(mid); + } + if mids.is_empty() { + log::info!(">>> Task {cid} completed and removed from registry"); + } + !mids.is_empty() + }); + return; + } + /// Get list of tasks a minion is involved in pub fn minion_tasks(&self, mid: &str) -> Vec { + let ongoing = match self.ongoing.lock() { + Ok(guard) => guard, + Err(e) => { + log::error!("Failed to acquire lock for task registry: {}", e); + return Vec::new(); + } + }; + let mut tasks = Vec::new(); - for (cid, mids) in self.ongoing.iter() { + for (cid, mids) in ongoing.iter() { if mids.contains(mid) { tasks.push(cid.clone()); } diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index 844700c3..06a9c177 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -315,13 +315,29 @@ impl SysMinion { match serde_json::from_value::(p.clone()) { Ok(ProtoValue::PingTypeGeneral) => { log::debug!("Received general ping from master"); - self.request(proto::msg::get_pong(ProtoValue::PingTypeGeneral)).await; + + let (loadavg, is_done, doneids) = { + let this = self.as_ptr(); + let mut ptc = this.pt_counter.lock().await; + let (l, d, i) = (ptc.get_loadaverage(), ptc.is_done(), ptc.get_done()); + if d { + ptc.flush_done(); + } + (l, d, i) + }; + + let pl = json!({ + "ld": loadavg, + "cd": if is_done { doneids } else { vec![] }, + }); + + self.request(proto::msg::get_pong(ProtoValue::PingTypeGeneral, Some(pl))).await; self.update_ping().await; } Ok(ProtoValue::PingTypeDiscovery) => { // XXX: On Discovery ping, we also send our traits and all the info for cluster log::debug!("Received discovery ping from master"); - self.request(proto::msg::get_pong(ProtoValue::PingTypeDiscovery)).await; + self.request(proto::msg::get_pong(ProtoValue::PingTypeDiscovery, None)).await; } Err(e) => { log::warn!("Invalid ping payload `{}`: {}", p, e); @@ -387,7 +403,6 @@ impl SysMinion { pub async fn send_fin_callback(self: Arc, ar: ActionResponse) -> Result<(), SysinspectError> { log::debug!("Sending fin sync callback on {}", ar.aid()); self.request(MinionMessage::new(self.get_minion_id(), RequestType::ModelEvent, json!(ar)).sendable()?).await; - self.as_ptr().pt_counter.lock().await.dec(); Ok(()) } @@ -525,13 +540,14 @@ impl SysMinion { match tokio::task::spawn_blocking(move || futures::executor::block_on(sr.start())).await { Ok(()) => { - self.as_ptr().pt_counter.lock().await.inc(); log::debug!("Sysinspect model cycle finished"); + log::info!("Task {} finished", cycle_id); } Err(e) => { log::error!("Blocking task crashed: {e}"); } }; + self.as_ptr().pt_counter.lock().await.dec(cycle_id); } } @@ -628,6 +644,7 @@ impl SysMinion { } // else: this minion is directly targeted by its Id. log::debug!("Through. {:?}", cmd.payload()); + self.as_ptr().pt_counter.lock().await.inc(cmd.cycle()); match PayloadType::try_from(cmd.payload().clone()) { Ok(PayloadType::ModelOrStatement(pld)) => { diff --git a/sysminion/src/proto.rs b/sysminion/src/proto.rs index d87e5200..a1362af2 100644 --- a/sysminion/src/proto.rs +++ b/sysminion/src/proto.rs @@ -4,7 +4,10 @@ pub mod msg { use crate::minion::MINION_SID; use libsysinspect::{ SysinspectError, - proto::{MasterMessage, ProtoConversion, rqtypes::ProtoValue}, + proto::{ + MasterMessage, ProtoConversion, + rqtypes::{ProtoKey, ProtoValue}, + }, }; use libsysinspect::{ proto::{MinionMessage, rqtypes::RequestType}, @@ -32,14 +35,23 @@ pub mod msg { } /// Make pong message - pub fn get_pong(t: ProtoValue) -> Vec { + pub fn get_pong(t: ProtoValue, payload: Option) -> Vec { let mut data: HashMap = HashMap::new(); - data.insert("pt".to_string(), to_value(t).unwrap()); - data.insert("sid".to_string(), to_value(MINION_SID.to_string()).unwrap()); + data.insert(ProtoKey::ProtoType.to_string(), to_value(t).unwrap()); + data.insert(ProtoKey::SessionId.to_string(), to_value(MINION_SID.to_string()).unwrap()); + data.insert( + ProtoKey::Payload.to_string(), + match payload { + Some(pl) => pl, + None => json!({}), + }, + ); + let p = MinionMessage::new(dataconv::as_str(traits::get_minion_traits(None).get(traits::SYS_ID)), RequestType::Pong, json!(data)); if let Ok(data) = p.sendable() { return data; } + vec![] } diff --git a/sysminion/src/ptcounter.rs b/sysminion/src/ptcounter.rs index 19dcf1b7..80708a6e 100644 --- a/sysminion/src/ptcounter.rs +++ b/sysminion/src/ptcounter.rs @@ -1,19 +1,25 @@ +use std::collections::HashSet; + use procfs::Current; /// Process and task counter. /// This is used to keep track of the number of processes and tasks /// running on the minion, to avoid overloading it. - +/// +/// Counter is also keeping track of done tasks and sends their cycle IDs +/// back to the master for bookkeeping when it reaches zero. Master can then +/// figure out what tasks were dropped/missed, if any. #[derive(Debug, Clone)] pub struct PTCounter { - tasks: usize, loadaverage: f32, + tasks: HashSet, // Cycle IDs of added tasks + done: HashSet, // Cycle IDs of done tasks } impl PTCounter { /// Create new counter pub fn new() -> Self { - Self { tasks: 0, loadaverage: 0.0 } + Self { tasks: HashSet::new(), done: HashSet::new(), loadaverage: 0.0 } } /// Update load average from /proc/loadavg @@ -26,22 +32,33 @@ impl PTCounter { } /// Increment task counter - pub fn inc(&mut self) { + pub fn inc(&mut self, cid: &str) { self.update_stats(); - self.tasks += 1; + self.tasks.insert(cid.to_string()); + log::info!("Added task {}, count increased to {}, load average: {}", cid, self.tasks.len(), self.loadaverage); } /// Decrement task counter - pub fn dec(&mut self) { - if self.tasks > 0 { - self.tasks -= 1; - } + pub fn dec(&mut self, cid: &str) { + self.tasks.remove(cid); + self.done.insert(cid.to_string()); self.update_stats(); + log::info!("Removed task {}, count decreased to {}, load average: {}", cid, self.tasks.len(), self.loadaverage); } /// Get current task count - pub fn get_tasks(&self) -> usize { - self.tasks + pub fn is_done(&self) -> bool { + self.tasks.is_empty() + } + + /// Get done task ids + /// This also clears the done set. + pub fn get_done(&mut self) -> Vec { + self.done.iter().cloned().collect::>() + } + + pub fn flush_done(&mut self) { + self.done.clear(); } /// Get current load average (5 min) From b1ebf87f52a19e56a986f856b486a3eaeb870f9b Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 14 Jan 2026 22:15:28 +0100 Subject: [PATCH 39/44] Remove superfluous ptr --- sysmaster/src/master.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 1e24487b..84f92958 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -410,7 +410,7 @@ impl SysMaster { return; } }; - guard.get_session().lock().await.ping(&c_id, Some(&pm.sid())); + guard.get_session().lock().await.ping(&c_id, Some(pm.sid())); let uptime = guard.get_session().lock().await.uptime(req.id()).unwrap_or_default(); log::info!("Update last contacted for {} (alive for {:.2} min)", req.id(), uptime as f64 / 60.0); From 0b99ac121338ba2fa5e5c8156233e64ccd06d534 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Wed, 14 Jan 2026 22:15:39 +0100 Subject: [PATCH 40/44] Remove unused return --- sysmaster/src/registry/taskreg.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/sysmaster/src/registry/taskreg.rs b/sysmaster/src/registry/taskreg.rs index 0f9d4e2c..23a586b7 100644 --- a/sysmaster/src/registry/taskreg.rs +++ b/sysmaster/src/registry/taskreg.rs @@ -81,7 +81,6 @@ impl TaskRegistry { } !mids.is_empty() }); - return; } /// Get list of tasks a minion is involved in From 6cbff2e39615f5de6b4395507fadc369a483f15f Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 15 Jan 2026 18:16:00 +0100 Subject: [PATCH 41/44] Bugfix setup on non-root environments. Add more information during setup. Disable root-only scenario. --- Cargo.lock | 1 + libsetup/Cargo.toml | 1 + libsetup/src/lib.rs | 6 ++++++ libsetup/src/mnsetup.rs | 43 +++++++++++++++++++++++++++++------------ 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dd6a7dc6..08bce1b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2991,6 +2991,7 @@ dependencies = [ name = "libsetup" version = "0.1.0" dependencies = [ + "colored", "libc", "libsysinspect", "log", diff --git a/libsetup/Cargo.toml b/libsetup/Cargo.toml index 6c3c2593..4897429f 100644 --- a/libsetup/Cargo.toml +++ b/libsetup/Cargo.toml @@ -11,3 +11,4 @@ serde_json = "1.0.145" serde_yaml = "0.9.34" uuid = { version = "1.19.0", features = ["v4"] } libc = { version = "0.2", features = ["extra_traits"] } +colored = "3.0.0" diff --git a/libsetup/src/lib.rs b/libsetup/src/lib.rs index bfbf1fbb..987de51a 100644 --- a/libsetup/src/lib.rs +++ b/libsetup/src/lib.rs @@ -12,8 +12,14 @@ pub fn get_ssh_client_ip() -> Option { let parts: Vec<&str> = ssh_client.split_whitespace().collect(); if !parts.is_empty() { return Some(parts[0].to_string()); + } else { + log::warn!("SSH_CLIENT variable is empty"); } + } else { + log::warn!("SSH_CLIENT environment variable not found"); } + log::error!("I was unable to determine the SSH client IP address. Have you use \"sudo\" without -E flag?"); + None } diff --git a/libsetup/src/mnsetup.rs b/libsetup/src/mnsetup.rs index 9c706b70..3e400c68 100644 --- a/libsetup/src/mnsetup.rs +++ b/libsetup/src/mnsetup.rs @@ -1,3 +1,4 @@ +use colored::Colorize; use libsysinspect::{ SysinspectError, cfg::mmconf::{ @@ -49,7 +50,7 @@ impl MinionSetup { format!("{}/{}", self.get_sharelib(), dir) } - fn check_my_permissions(&self) -> Result<(), SysinspectError> { + fn check_my_permissions() -> Result<(), SysinspectError> { if unsafe { libc::getuid() } != 0 { return Err(SysinspectError::ConfigError("SysMinion must be run as root".to_string())); } @@ -139,9 +140,8 @@ impl MinionSetup { self.cfg.set_reconnect_interval("1"); // String, because it can be an expression like "1-5" (random between 1 and 5) let cfp = PathBuf::from(self.get_etc()).join("sysinspect.conf"); - log::info!("Writing configuration file to {}", cfp.to_str().unwrap_or_default()); - - fs::write(cfp, SysInspectConfig::default().set_minion_config(self.cfg.clone()).to_yaml())?; + fs::write(&cfp, SysInspectConfig::default().set_minion_config(self.cfg.clone()).to_yaml())?; + log::info!("πŸ“„ Configuration file written to {}", cfp.to_str().unwrap_or_default().bright_white().bold()); Ok(()) } @@ -200,25 +200,44 @@ impl MinionSetup { /// Setup the minion pub fn setup(&mut self) -> Result<(), SysinspectError> { - log::info!("Setting up the minion. This is a quick setup, please check the configuration files for more details."); + log::info!("βš™οΈ Setting up the minion. This is a quick setup, please check the configuration files for more details."); - self.check_my_permissions()?; - log::info!("Permissions OK"); + match Self::check_my_permissions() { + Ok(_) => { + log::info!("Permissions OK"); + } + Err(_) => { + log::warn!( + "🚨 {} is installed as {}, so its operation {}. For full functionality, you have to setup {} as {}.", + "SysMinion".bold(), + "non-root".bright_red(), + "might be limited".bright_yellow(), + "SysMinion".bold(), + "root".bright_green(), + ); + log::warn!( + "🚨 Don't forget to use \"{}\" to preserve environment variables, when setting up {} as {}.", + "sudo -E".yellow(), + "SysMinion".bold(), + "root".bright_green() + ); + } + } self.check_dir_structure()?; - log::info!("Directory structure OK"); + log::info!("πŸ“‚ Directory structure OK"); self.generate_dir_structure()?; - log::info!("Directory structure set up"); + log::info!("πŸ“‚ Directory structure set up"); self.generate_config()?; - log::info!("Configuration file generated to {}", self.get_etc()); + log::info!("πŸ“„ Configuration file generated to {}", self.get_etc().bright_white().bold()); self.copy_binaries()?; - log::info!("Binaries are copied to {}", self.get_bin()); + log::info!("πŸ“¦ Binaries are copied to {}", self.get_bin().bright_white().bold()); self.cleanup()?; - log::info!("That should do."); + log::info!("πŸ‘Œ That should do."); Ok(()) } From 2cd237bd0561d02fb17a54ccf3cd8271bd6a4718 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 16 Jan 2026 13:03:01 +0100 Subject: [PATCH 42/44] Update cluster stats with the disk IO pressure --- libsysinspect/src/proto/payload.rs | 8 ++ sysmaster/src/cluster.rs | 192 ++++++++++++++++++++++---- sysmaster/src/master.rs | 13 +- sysminion/src/minion.rs | 22 ++- sysminion/src/ptcounter.rs | 213 +++++++++++++++++++++++++++-- 5 files changed, 398 insertions(+), 50 deletions(-) diff --git a/libsysinspect/src/proto/payload.rs b/libsysinspect/src/proto/payload.rs index 2f7085d9..aa8f4c8c 100644 --- a/libsysinspect/src/proto/payload.rs +++ b/libsysinspect/src/proto/payload.rs @@ -126,6 +126,9 @@ pub struct PingPayload { #[serde(rename = "cd")] completed: Vec, // completed task ids + + #[serde(rename = "dbps")] + disk_write_bps: f64, // disk write bytes per second } impl PingPayload { @@ -138,4 +141,9 @@ impl PingPayload { pub fn completed(&self) -> &Vec { &self.completed } + + /// Get disk write bps + pub fn disk_write_bps(&self) -> f64 { + self.disk_write_bps + } } diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index effd089d..6f3eb8f9 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -1,5 +1,4 @@ // Cluster management for sysmaster - use crate::registry::{mreg::MinionRegistry, session::SessionKeeper, taskreg::TaskRegistry}; use globset::Glob; use libsysinspect::{SysinspectError, cfg::mmconf::ClusteredMinion, proto::MasterMessage}; @@ -11,18 +10,22 @@ use std::{ }; use tokio::sync::Mutex; +const DEFAULT_TASK_JITTER: usize = 3; // Task tolerance + #[derive(Debug, Clone, Default)] /// Representation of a clustered node pub struct ClusterNode { mid: String, traits: HashMap, + load_average: f32, + io_bps: f64, // disk I/O in bytes per second on writes } /// Cluster node representation impl ClusterNode { /// Create a new cluster node pub fn new(mid: &str, traits: HashMap) -> ClusterNode { - ClusterNode { mid: mid.to_string(), traits } + ClusterNode { mid: mid.to_string(), traits, load_average: 0.0, io_bps: 0.0 } } /// Match hostname with glob pattern @@ -52,6 +55,16 @@ impl ClusterNode { } true } + + /// Update load average + pub fn set_load_average(&mut self, la: f32) { + self.load_average = la; + } + + /// Update I/O bps + pub fn set_io_bps(&mut self, bps: f64) { + self.io_bps = bps; + } } #[derive(Debug, Clone, Default)] @@ -86,17 +99,14 @@ pub struct VirtualMinionsCluster { cfg: Vec, task_tracker: Arc>, virtual_minions: Vec, // Configured clustered minions - /* - Here must be a sort of state of the cluster, e.g., which minion is - online, which is offline, last heartbeat time, their current load etc. - */ + task_tolerance: usize, } impl VirtualMinionsCluster { pub fn new( cfg: Vec, mreg: Arc>, session: Arc>, task_tracker: Arc>, ) -> VirtualMinionsCluster { - VirtualMinionsCluster { virtual_minions: Vec::new(), mreg, cfg, session, task_tracker } + VirtualMinionsCluster { virtual_minions: Vec::new(), mreg, cfg, session, task_tracker, task_tolerance: DEFAULT_TASK_JITTER } } fn filter_traits(&self, traits: &HashMap) -> HashMap { @@ -110,6 +120,11 @@ impl VirtualMinionsCluster { filtered } + fn normalise_weights_percent(values: &[(String, f64)]) -> HashMap { + let total: f64 = values.iter().map(|(_, v)| *v).sum(); + values.iter().map(|(id, v)| (id.clone(), 100.0 * (*v / total.max(1e-6)))).collect() + } + pub async fn init(&mut self) -> Result<(), SysinspectError> { let mut mreg = self.mreg.lock().await; @@ -205,44 +220,147 @@ impl VirtualMinionsCluster { mids } - /// Decide the best-fit minion for the given query. + /// Decide the best-fit minion for a task based on current load and I/O pressure pub async fn decide(&self, query: &str) -> Option { let mids = self.query_mids(query); - let mut fmid: Option = None; - let mut fmidt: usize = usize::MAX; + if mids.is_empty() { + return None; + } - for mid in mids.iter() { - // Skip offline minions - if !self.session.lock().await.alive(mid) { + // find alive minions + let mut alive: Vec = Vec::new(); + { + let mut session = self.session.lock().await; + for mid in mids.iter() { + if session.alive(mid) { + alive.push(mid.clone()); + } + } + } + + if alive.is_empty() { + return None; + } + + // get IO rates + let mut rates: Vec<(String, f64)> = Vec::with_capacity(alive.len()); + for mid in alive.iter() { + let mut bps = 0.0; + 'outer: for vm in self.virtual_minions.iter() { + for m in vm.minions.iter() { + if m.mid == *mid { + bps = m.io_bps; + break 'outer; + } + } + } + rates.push((mid.clone(), bps.max(0.0))); + } + + let weights: HashMap = Self::normalise_weights_percent(&rates); + + // minimum task count and lowest disk weight + let tracker = self.task_tracker.lock().await; + + let mut min_tasks = usize::MAX; + for mid in alive.iter() { + min_tasks = min_tasks.min(tracker.minion_tasks(mid).len()); + } + let cutoff = min_tasks.saturating_add(self.task_tolerance); + + let mut best_mid: Option = None; + let mut best_weight: f64 = f64::MAX; + let mut best_tasks: usize = usize::MAX; + + for mid in alive.iter() { + let tasks = tracker.minion_tasks(mid).len(); + if tasks > cutoff { continue; } - let t = self.task_tracker.lock().await.minion_tasks(mid).len(); - if t == 0 { - fmid = Some(mid.to_string()); - break; + let w = *weights.get(mid).unwrap_or(&0.0); + + // lower disk weight, then fewer tasks if weights tie + if w < best_weight || (w == best_weight && tasks < best_tasks) { + best_weight = w; + best_tasks = tasks; + best_mid = Some(mid.clone()); + } + } + + let fmid = best_mid?; + let mrec = match self.mreg.lock().await.get(&fmid) { + Ok(r) => r, + Err(err) => { + log::error!("Unable to get minion record for {fmid}: {err}"); + return None; } + }; + + mrec.and_then(|rec| rec.get_traits().get("system.hostname.fqdn").and_then(|v| v.as_str()).map(|s| s.to_string())) + } + + pub async fn _decide(&self, query: &str) -> Option { + let mids = self.query_mids(query); - if t < fmidt { - fmidt = t; - fmid = Some(mid.to_string()); + // Filter to alive mids first + let mut alive: Vec = Vec::new(); + for mid in mids.iter() { + if self.session.lock().await.alive(mid) { + alive.push(mid.clone()); } } + if alive.is_empty() { + return None; + } - if let Some(fmid) = fmid { - let r = match self.mreg.lock().await.get(&fmid) { - Ok(mrec) => mrec, - Err(err) => { - log::error!("Unable to get minion record for {fmid}: {err}"); - return None; + // Build write rates for normalization from cluster state + let mut rates: Vec<(String, f64)> = Vec::new(); + for mid in alive.iter() { + // pull write_bps from stored ClusterNode + let mut bps = 0.0; + 'outer: for vm in self.virtual_minions.iter() { + for m in vm.minions.iter() { + if &m.mid == mid { + bps = m.io_bps; + break 'outer; + } } - }; - if let Some(r) = r { - return r.get_traits().get("system.hostname.fqdn").and_then(|v| v.as_str()).map(|s| s.to_string()); } + rates.push((mid.clone(), bps.max(0.0))); } + let weights = Self::normalise_weights_percent(&rates); - None + // Now score each minion: tasks first, then disk weight + let tracker = self.task_tracker.lock().await; + + let mut best_mid: Option = None; + let mut best_tasks: usize = usize::MAX; + let mut best_weight: f64 = f64::MAX; + + for mid in alive.iter() { + let tasks = tracker.minion_tasks(mid).len(); + let w = *weights.get(mid).unwrap_or(&0.0); + + if tasks + self.task_tolerance < best_tasks || (tasks <= best_tasks + self.task_tolerance && w < best_weight) { + best_tasks = tasks; + best_weight = w; + best_mid = Some(mid.clone()); + } + } + + let fmid = best_mid?; + + // return fqdn like before + let r = match self.mreg.lock().await.get(&fmid) { + Ok(mrec) => mrec, + Err(err) => { + log::error!("Unable to get minion record for {fmid}: {err}"); + return None; + } + }; + + r.and_then(|rec| rec.get_traits().get("system.hostname.fqdn").and_then(|v| v.as_str()).map(|s| s.to_string())) } /// Call a query on the clustered minion. @@ -283,4 +401,18 @@ impl VirtualMinionsCluster { hostnames } + + /// Update a physical minion stats, no matter where it belongs to + pub fn update_stats(&mut self, mid: &str, load_average: f32, io_bps: f64) { + for vm in self.virtual_minions.iter_mut() { + for m in vm.minions.iter_mut() { + if m.mid == mid { + log::info!("Updating load average for minion {}: {}, I/O bps: {}", mid, load_average, io_bps); + m.set_load_average(load_average); + m.set_io_bps(io_bps); + return; + } + } + } + } } diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 84f92958..7b7297ee 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -234,10 +234,10 @@ impl SysMaster { if is_virtual && let Some(decided) = self.vmcluster.decide(&query).await { log::warn!(">>> Decided to run on: {}", decided); tgt.add_hostname(&decided); - } - - for hostname in hostnames.iter() { - tgt.add_hostname(hostname); + } else { + for hostname in hostnames.iter() { + tgt.add_hostname(hostname); + } } log::debug!("Target: {:#?}", tgt); @@ -401,8 +401,8 @@ impl SysMaster { let c_master = Arc::clone(&master); let c_id = req.id().to_string(); tokio::spawn(async move { - log::info!("Received pong from {:#?}", req.payload()); - let guard = c_master.lock().await; + log::debug!("Received pong from {:#?}", req.payload()); + let mut guard = c_master.lock().await; let pm = match PingData::from_value(req.payload().clone()) { Ok(pm) => pm, Err(err) => { @@ -414,6 +414,7 @@ impl SysMaster { let uptime = guard.get_session().lock().await.uptime(req.id()).unwrap_or_default(); log::info!("Update last contacted for {} (alive for {:.2} min)", req.id(), uptime as f64 / 60.0); + guard.vmcluster.update_stats(&c_id, pm.payload().load_average(), pm.payload().disk_write_bps()); // Update task tracker let taskreg = guard.get_task_registry(); diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index 06a9c177..228a561d 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -213,6 +213,18 @@ impl SysMinion { Ok(()) } + pub async fn do_stats_update(self: Arc) -> Result<(), SysinspectError> { + tokio::spawn(async move { + loop { + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + let this = self.as_ptr(); + let mut ptc = this.pt_counter.lock().await; + ptc.update_stats(); + } + }); + Ok(()) + } + pub async fn do_proto(self: Arc) -> Result<(), SysinspectError> { let rstm = Arc::clone(&self.rstm); @@ -314,21 +326,22 @@ impl SysMinion { match serde_json::from_value::(p.clone()) { Ok(ProtoValue::PingTypeGeneral) => { - log::debug!("Received general ping from master"); + log::info!("Received general ping from master"); - let (loadavg, is_done, doneids) = { + let (loadavg, is_done, doneids, io_bps) = { let this = self.as_ptr(); let mut ptc = this.pt_counter.lock().await; - let (l, d, i) = (ptc.get_loadaverage(), ptc.is_done(), ptc.get_done()); + let (l, d, i, io) = (ptc.get_loadaverage(), ptc.is_done(), ptc.get_done(), ptc.get_io_bps()); if d { ptc.flush_done(); } - (l, d, i) + (l, d, i, io) }; let pl = json!({ "ld": loadavg, "cd": if is_done { doneids } else { vec![] }, + "dbps": io_bps, }); self.request(proto::msg::get_pong(ProtoValue::PingTypeGeneral, Some(pl))).await; @@ -683,6 +696,7 @@ async fn _minion_instance(cfg: MinionConfig, fingerprint: Option) -> Res } minion.as_ptr().do_ping_update(state.clone()).await?; + minion.as_ptr().do_stats_update().await?; // Keeps client running while !state.exit.load(std::sync::atomic::Ordering::Relaxed) { diff --git a/sysminion/src/ptcounter.rs b/sysminion/src/ptcounter.rs index 80708a6e..bd2f5a2f 100644 --- a/sysminion/src/ptcounter.rs +++ b/sysminion/src/ptcounter.rs @@ -1,6 +1,58 @@ -use std::collections::HashSet; - use procfs::Current; +use std::{ + collections::{HashMap, HashSet}, + fs, + path::Path, + time::Instant, +}; +use sysinfo::{DiskKind, Disks, System}; + +#[derive(Debug, Clone)] +struct DiskStats { + device: String, + mountpoint: String, + + // absolute counters since boot (kernel) + read_bytes: u64, + write_bytes: u64, + + // derived values over last sample window + read_delta: u64, + write_delta: u64, + read_bps: f64, + write_bps: f64, + + initialized: bool, +} + +impl DiskStats { + fn new(device: String, mountpoint: String) -> Self { + Self { device, mountpoint, read_bytes: 0, write_bytes: 0, read_delta: 0, write_delta: 0, read_bps: 0.0, write_bps: 0.0, initialized: false } + } + + fn sample(&mut self, new_read: u64, new_write: u64, dt_secs: f64) { + if !self.initialized { + self.read_bytes = new_read; + self.write_bytes = new_write; + self.initialized = true; + self.read_delta = 0; + self.write_delta = 0; + self.read_bps = 0.0; + self.write_bps = 0.0; + return; + } + + self.read_delta = new_read.saturating_sub(self.read_bytes); + self.write_delta = new_write.saturating_sub(self.write_bytes); + + self.read_bytes = new_read; + self.write_bytes = new_write; + + let dt = dt_secs.max(1e-9); + self.read_bps = self.read_delta as f64 / dt; + self.write_bps = self.write_delta as f64 / dt; + } +} /// Process and task counter. /// This is used to keep track of the number of processes and tasks @@ -9,9 +61,19 @@ use procfs::Current; /// Counter is also keeping track of done tasks and sends their cycle IDs /// back to the master for bookkeeping when it reaches zero. Master can then /// figure out what tasks were dropped/missed, if any. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct PTCounter { + sys: System, + disks: Disks, + + // System stats + cpu_usage: f32, + active_processes: usize, loadaverage: f32, + disk_stats: Vec, + last_stats_ts: Option, + + // Minion stats tasks: HashSet, // Cycle IDs of added tasks done: HashSet, // Cycle IDs of done tasks } @@ -19,21 +81,134 @@ pub struct PTCounter { impl PTCounter { /// Create new counter pub fn new() -> Self { - Self { tasks: HashSet::new(), done: HashSet::new(), loadaverage: 0.0 } + let mut sys = System::new_all(); + sys.refresh_all(); + + let mut disks = Disks::new(); + disks.refresh(true); + + Self { + sys, + tasks: HashSet::new(), + done: HashSet::new(), + loadaverage: 0.0, + cpu_usage: 0.0, + active_processes: 0, + disks, + disk_stats: Vec::new(), + last_stats_ts: None, + } + } + + fn mapper_to_dm_kname(dev: &str) -> Option { + // dev like "/dev/mapper/ubuntu--vg-root" + let name = dev.strip_prefix("/dev/mapper/")?; + + // /sys/class/block/dm-*/dm/name contains the mapper name + let sys_block = Path::new("/sys/class/block"); + for entry in fs::read_dir(sys_block).ok()? { + let entry = entry.ok()?; + let fname = entry.file_name(); + let kname = fname.to_string_lossy(); + if !kname.starts_with("dm-") { + continue; + } + + let dm_name_path = entry.path().join("dm").join("name"); + if let Ok(dm_name) = fs::read_to_string(dm_name_path) + && dm_name.trim() == name { + return Some(kname.to_string()); // "dm-0" + } + } + None + } + + fn device_to_kname(dev: &str) -> Option { + if dev.starts_with("/dev/mapper/") { Self::mapper_to_dm_kname(dev) } else { Some(dev.strip_prefix("/dev/").unwrap_or(dev).to_string()) } + } + + fn diskstats_bytes() -> procfs::ProcResult> { + let mut map = HashMap::new(); + for ds in procfs::diskstats()? { + // sectors -> bytes (Linux diskstats sectors are 512-byte units in practice) + let read_bytes = ds.sectors_read * 512; + let write_bytes = ds.sectors_written * 512; + map.insert(ds.name, (read_bytes, write_bytes)); + } + Ok(map) } - /// Update load average from /proc/loadavg - /// This is called internally on each inc/dec operation. - /// More stats will/should be added here. - fn update_stats(&mut self) { + /// Update system stats + /// Called periodically in the minion in a separate thread to refresh stats + pub(crate) fn update_stats(&mut self) { + // loadavg if let Ok(la) = procfs::LoadAverage::current() { self.loadaverage = la.five; } + + // cpu + processes + self.sys.refresh_cpu_all(); + self.sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true); + self.cpu_usage = self.sys.global_cpu_usage(); + self.active_processes = self.sys.processes().len(); + + // dt for rates + let now = Instant::now(); + let dt_secs = self.last_stats_ts.map(|t| now.duration_since(t).as_secs_f64()).unwrap_or(0.0); + self.last_stats_ts = Some(now); + + // Read monotonic disk counters ONCE + let io = match Self::diskstats_bytes() { + Ok(v) => v, + Err(e) => { + log::warn!("Failed to read /proc/diskstats: {e}"); + return; + } + }; + + // Refresh disk list (for device names + mountpoints) + self.disks.refresh(true); + + for d in self.disks.list().iter().filter(|d| matches!(d.kind(), DiskKind::HDD | DiskKind::SSD)) { + let device = d.name().to_string_lossy().to_string(); // e.g. "/dev/nvme0n1p2" or "/dev/mapper/..." + let mountpoint = d.mount_point().to_string_lossy().to_string(); + + // Try to match device to diskstats key + let kname = match Self::device_to_kname(&device) { + Some(k) => k, + None => continue, + }; + + let (new_read, new_write) = match io.get(&kname) { + Some(v) => *v, + None => continue, + }; + + match self.disk_stats.iter_mut().find(|ds| ds.device == device) { + Some(ds) => ds.sample(new_read, new_write, dt_secs), + None => { + let mut ds = DiskStats::new(device, mountpoint); + ds.sample(new_read, new_write, dt_secs); + self.disk_stats.push(ds); + } + } + } + + // Top writers without cloning everything like a potato + let mut top: Vec<&DiskStats> = self.disk_stats.iter().collect(); + top.sort_by(|a, b| b.write_bps.partial_cmp(&a.write_bps).unwrap_or(std::cmp::Ordering::Equal)); + + log::info!( + "Stats: loadavg(5m)={:.2}, cpu={:.1}%, procs={}, top_writers={:#?}", + self.loadaverage, + self.cpu_usage, + self.active_processes, + top.into_iter().take(3).collect::>() + ); } /// Increment task counter pub fn inc(&mut self, cid: &str) { - self.update_stats(); self.tasks.insert(cid.to_string()); log::info!("Added task {}, count increased to {}, load average: {}", cid, self.tasks.len(), self.loadaverage); } @@ -42,7 +217,6 @@ impl PTCounter { pub fn dec(&mut self, cid: &str) { self.tasks.remove(cid); self.done.insert(cid.to_string()); - self.update_stats(); log::info!("Removed task {}, count decreased to {}, load average: {}", cid, self.tasks.len(), self.loadaverage); } @@ -65,4 +239,23 @@ impl PTCounter { pub fn get_loadaverage(&self) -> f32 { self.loadaverage } + + /// Get current CPU usage (%) + pub fn get_cpu_usage(&self) -> f32 { + self.cpu_usage + } + + /// Get current active process count + pub fn get_active_processes(&self) -> usize { + self.active_processes + } + + /// Current write pressure of this minion (bytes/sec). + /// Picks the "/" mount if present; otherwise falls back to max writer. + pub fn get_io_bps(&self) -> f64 { + if let Some(root) = self.disk_stats.iter().find(|ds| ds.mountpoint == "/") { + return root.write_bps; + } + self.disk_stats.iter().map(|ds| ds.write_bps).fold(0.0, f64::max) + } } From 0e08b111d3fc09e2871549b75162fbf05e76f8da Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 16 Jan 2026 15:21:04 +0100 Subject: [PATCH 43/44] Add CPU usage and load avg factors --- libsysinspect/src/proto/payload.rs | 8 ++++ sysmaster/src/cluster.rs | 74 ++++-------------------------- sysmaster/src/master.rs | 11 +++-- sysminion/src/minion.rs | 8 ++-- sysminion/src/ptcounter.rs | 7 +-- 5 files changed, 33 insertions(+), 75 deletions(-) diff --git a/libsysinspect/src/proto/payload.rs b/libsysinspect/src/proto/payload.rs index aa8f4c8c..5f3f2644 100644 --- a/libsysinspect/src/proto/payload.rs +++ b/libsysinspect/src/proto/payload.rs @@ -129,6 +129,9 @@ pub struct PingPayload { #[serde(rename = "dbps")] disk_write_bps: f64, // disk write bytes per second + + #[serde(rename = "cpu")] + cpu_usage: f32, // CPU usage percentage } impl PingPayload { @@ -146,4 +149,9 @@ impl PingPayload { pub fn disk_write_bps(&self) -> f64 { self.disk_write_bps } + + /// Get CPU usage + pub fn cpu_usage(&self) -> f32 { + self.cpu_usage + } } diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index 6f3eb8f9..d87b8626 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -1,5 +1,6 @@ // Cluster management for sysmaster use crate::registry::{mreg::MinionRegistry, session::SessionKeeper, taskreg::TaskRegistry}; +use colored::Colorize; use globset::Glob; use libsysinspect::{SysinspectError, cfg::mmconf::ClusteredMinion, proto::MasterMessage}; use serde_json::Value; @@ -18,14 +19,15 @@ pub struct ClusterNode { mid: String, traits: HashMap, load_average: f32, - io_bps: f64, // disk I/O in bytes per second on writes + io_bps: f64, // disk I/O in bytes per second on writes + cpu_usage: f32, // CPU usage percentage (overall) } /// Cluster node representation impl ClusterNode { /// Create a new cluster node pub fn new(mid: &str, traits: HashMap) -> ClusterNode { - ClusterNode { mid: mid.to_string(), traits, load_average: 0.0, io_bps: 0.0 } + ClusterNode { mid: mid.to_string(), traits, load_average: 0.0, io_bps: 0.0, cpu_usage: 0.0 } } /// Match hostname with glob pattern @@ -65,6 +67,11 @@ impl ClusterNode { pub fn set_io_bps(&mut self, bps: f64) { self.io_bps = bps; } + + /// Update CPU usage + pub fn set_cpu_usage(&mut self, cpu: f32) { + self.cpu_usage = cpu; + } } #[derive(Debug, Clone, Default)] @@ -300,69 +307,6 @@ impl VirtualMinionsCluster { mrec.and_then(|rec| rec.get_traits().get("system.hostname.fqdn").and_then(|v| v.as_str()).map(|s| s.to_string())) } - pub async fn _decide(&self, query: &str) -> Option { - let mids = self.query_mids(query); - - // Filter to alive mids first - let mut alive: Vec = Vec::new(); - for mid in mids.iter() { - if self.session.lock().await.alive(mid) { - alive.push(mid.clone()); - } - } - if alive.is_empty() { - return None; - } - - // Build write rates for normalization from cluster state - let mut rates: Vec<(String, f64)> = Vec::new(); - for mid in alive.iter() { - // pull write_bps from stored ClusterNode - let mut bps = 0.0; - 'outer: for vm in self.virtual_minions.iter() { - for m in vm.minions.iter() { - if &m.mid == mid { - bps = m.io_bps; - break 'outer; - } - } - } - rates.push((mid.clone(), bps.max(0.0))); - } - let weights = Self::normalise_weights_percent(&rates); - - // Now score each minion: tasks first, then disk weight - let tracker = self.task_tracker.lock().await; - - let mut best_mid: Option = None; - let mut best_tasks: usize = usize::MAX; - let mut best_weight: f64 = f64::MAX; - - for mid in alive.iter() { - let tasks = tracker.minion_tasks(mid).len(); - let w = *weights.get(mid).unwrap_or(&0.0); - - if tasks + self.task_tolerance < best_tasks || (tasks <= best_tasks + self.task_tolerance && w < best_weight) { - best_tasks = tasks; - best_weight = w; - best_mid = Some(mid.clone()); - } - } - - let fmid = best_mid?; - - // return fqdn like before - let r = match self.mreg.lock().await.get(&fmid) { - Ok(mrec) => mrec, - Err(err) => { - log::error!("Unable to get minion record for {fmid}: {err}"); - return None; - } - }; - - r.and_then(|rec| rec.get_traits().get("system.hostname.fqdn").and_then(|v| v.as_str()).map(|s| s.to_string())) - } - /// Call a query on the clustered minion. /// This will do the following: /// 1. Drop offline minions, if any (based on session state) diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index 7b7297ee..f91d0fb7 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -410,11 +410,14 @@ impl SysMaster { return; } }; - guard.get_session().lock().await.ping(&c_id, Some(pm.sid())); - let uptime = guard.get_session().lock().await.uptime(req.id()).unwrap_or_default(); - log::info!("Update last contacted for {} (alive for {:.2} min)", req.id(), uptime as f64 / 60.0); - guard.vmcluster.update_stats(&c_id, pm.payload().load_average(), pm.payload().disk_write_bps()); + guard.get_session().lock().await.ping(&c_id, Some(pm.sid())); + guard.vmcluster.update_stats( + &c_id, + pm.payload().load_average(), + pm.payload().disk_write_bps(), + pm.payload().cpu_usage(), + ); // Update task tracker let taskreg = guard.get_task_registry(); diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index 228a561d..7cccd34c 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -328,20 +328,22 @@ impl SysMinion { Ok(ProtoValue::PingTypeGeneral) => { log::info!("Received general ping from master"); - let (loadavg, is_done, doneids, io_bps) = { + let (loadavg, is_done, doneids, io_bps, cpu_usage) = { let this = self.as_ptr(); let mut ptc = this.pt_counter.lock().await; - let (l, d, i, io) = (ptc.get_loadaverage(), ptc.is_done(), ptc.get_done(), ptc.get_io_bps()); + let (l, d, i, io, cpu) = + (ptc.get_loadaverage(), ptc.is_done(), ptc.get_done(), ptc.get_io_bps(), ptc.get_cpu_usage()); if d { ptc.flush_done(); } - (l, d, i, io) + (l, d, i, io, cpu) }; let pl = json!({ "ld": loadavg, "cd": if is_done { doneids } else { vec![] }, "dbps": io_bps, + "cpu": cpu_usage, }); self.request(proto::msg::get_pong(ProtoValue::PingTypeGeneral, Some(pl))).await; diff --git a/sysminion/src/ptcounter.rs b/sysminion/src/ptcounter.rs index bd2f5a2f..15200e3b 100644 --- a/sysminion/src/ptcounter.rs +++ b/sysminion/src/ptcounter.rs @@ -116,9 +116,10 @@ impl PTCounter { let dm_name_path = entry.path().join("dm").join("name"); if let Ok(dm_name) = fs::read_to_string(dm_name_path) - && dm_name.trim() == name { - return Some(kname.to_string()); // "dm-0" - } + && dm_name.trim() == name + { + return Some(kname.to_string()); // "dm-0" + } } None } From 59138d2a69e001202c68993014809a784648e213 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Fri, 16 Jan 2026 15:21:23 +0100 Subject: [PATCH 44/44] Order log levels --- sysmaster/src/cluster.rs | 14 ++++++++++---- sysmaster/src/master.rs | 2 +- sysmaster/src/registry/taskreg.rs | 12 ++++++------ sysminion/src/minion.rs | 2 -- sysminion/src/ptcounter.rs | 4 ++-- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/sysmaster/src/cluster.rs b/sysmaster/src/cluster.rs index d87b8626..b3e974c7 100644 --- a/sysmaster/src/cluster.rs +++ b/sysmaster/src/cluster.rs @@ -183,7 +183,12 @@ impl VirtualMinionsCluster { if self.virtual_minions.is_empty() { log::warn!("No clustered minions configured or found in the cluster."); } else { - log::info!("Clustered minion details: {:#?}", self.virtual_minions); + log::info!( + "Initialized {} clustered minion{}.", + self.virtual_minions.len().to_string().bright_green().bold(), + if self.virtual_minions.len() == 1 { "" } else { "s" } + ); + log::debug!("Clustered minion details: {:#?}", self.virtual_minions); } Ok(()) @@ -339,7 +344,7 @@ impl VirtualMinionsCluster { hostnames.push(hn.to_string()); } } else { - log::info!("Minion {} is offline, skipping", mid); + log::debug!("Minion {} is offline, skipping", mid); } } @@ -347,13 +352,14 @@ impl VirtualMinionsCluster { } /// Update a physical minion stats, no matter where it belongs to - pub fn update_stats(&mut self, mid: &str, load_average: f32, io_bps: f64) { + pub fn update_stats(&mut self, mid: &str, load_average: f32, io_bps: f64, cpu_usage: f32) { for vm in self.virtual_minions.iter_mut() { for m in vm.minions.iter_mut() { if m.mid == mid { - log::info!("Updating load average for minion {}: {}, I/O bps: {}", mid, load_average, io_bps); + log::debug!("Updating load average for minion {}: {}, I/O bps: {}, CPU usage: {}", mid, load_average, io_bps, cpu_usage); m.set_load_average(load_average); m.set_io_bps(io_bps); + m.set_cpu_usage(cpu_usage); return; } } diff --git a/sysmaster/src/master.rs b/sysmaster/src/master.rs index f91d0fb7..bba23ae1 100644 --- a/sysmaster/src/master.rs +++ b/sysmaster/src/master.rs @@ -232,7 +232,7 @@ impl SysMaster { tgt.set_context_query(context); if is_virtual && let Some(decided) = self.vmcluster.decide(&query).await { - log::warn!(">>> Decided to run on: {}", decided); + log::debug!("Virtual minion requested. Decided to run on a physical: {}", decided.bright_yellow()); tgt.add_hostname(&decided); } else { for hostname in hostnames.iter() { diff --git a/sysmaster/src/registry/taskreg.rs b/sysmaster/src/registry/taskreg.rs index 23a586b7..48ebf75a 100644 --- a/sysmaster/src/registry/taskreg.rs +++ b/sysmaster/src/registry/taskreg.rs @@ -37,7 +37,7 @@ impl TaskRegistry { }; ongoing.insert(cid.to_string(), mids.into_iter().collect()); - log::info!(">>> Registered task {cid} with targeted minions: {:#?}", ongoing.get(cid)); + log::debug!("Registered task {cid} with targeted minions: {:#?}", ongoing.get(cid)); } /// Deregister a minion ID from a task @@ -51,17 +51,17 @@ impl TaskRegistry { }; if let Some(minions) = ongoing.get_mut(cid) { minions.remove(mid); - log::info!(">>> Deregistered minion {mid} from task {cid}. Remaining minions: {:#?}", minions); + log::debug!("Deregistered minion {mid} from task {cid}. Remaining minions: {:#?}", minions); if minions.is_empty() { ongoing.remove(cid); - log::info!(">>> Task {cid} completed and removed from registry"); + log::debug!("Task {cid} completed and removed from registry"); } } } pub fn flush(&mut self, mid: &str, cids: &Vec) { for cid in cids { - log::info!(">>> Flushing minion {mid} from task {cid}"); + log::debug!("Flushing minion {mid} from task {cid}"); } let mut ongoing = match self.ongoing.lock() { @@ -73,11 +73,11 @@ impl TaskRegistry { }; ongoing.retain(|cid, mids| { if mids.contains(mid) { - log::info!(">>> Flushing minion {mid} from task {cid}"); + log::debug!("Flushing minion {mid} from task {cid}"); mids.remove(mid); } if mids.is_empty() { - log::info!(">>> Task {cid} completed and removed from registry"); + log::debug!("Task {cid} completed and removed from registry"); } !mids.is_empty() }); diff --git a/sysminion/src/minion.rs b/sysminion/src/minion.rs index 7cccd34c..bdd385db 100644 --- a/sysminion/src/minion.rs +++ b/sysminion/src/minion.rs @@ -326,8 +326,6 @@ impl SysMinion { match serde_json::from_value::(p.clone()) { Ok(ProtoValue::PingTypeGeneral) => { - log::info!("Received general ping from master"); - let (loadavg, is_done, doneids, io_bps, cpu_usage) = { let this = self.as_ptr(); let mut ptc = this.pt_counter.lock().await; diff --git a/sysminion/src/ptcounter.rs b/sysminion/src/ptcounter.rs index 15200e3b..2f2cffc7 100644 --- a/sysminion/src/ptcounter.rs +++ b/sysminion/src/ptcounter.rs @@ -162,7 +162,7 @@ impl PTCounter { let io = match Self::diskstats_bytes() { Ok(v) => v, Err(e) => { - log::warn!("Failed to read /proc/diskstats: {e}"); + log::error!("Failed to read /proc/diskstats: {e}"); return; } }; @@ -199,7 +199,7 @@ impl PTCounter { let mut top: Vec<&DiskStats> = self.disk_stats.iter().collect(); top.sort_by(|a, b| b.write_bps.partial_cmp(&a.write_bps).unwrap_or(std::cmp::Ordering::Equal)); - log::info!( + log::debug!( "Stats: loadavg(5m)={:.2}, cpu={:.1}%, procs={}, top_writers={:#?}", self.loadaverage, self.cpu_usage,