From b20f8e8b68dcd16fc6ec84fc35b063db1b9d89e6 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Thu, 10 Aug 2023 13:14:54 -0700 Subject: [PATCH 1/3] Split running from returning commands, add pfexec --- src/smf.rs | 291 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 220 insertions(+), 71 deletions(-) diff --git a/src/smf.rs b/src/smf.rs index e47043a..50ab5bb 100644 --- a/src/smf.rs +++ b/src/smf.rs @@ -5,6 +5,7 @@ #![deny(missing_docs)] use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; use std::str::FromStr; use std::string::ToString; use thiserror::Error; @@ -14,6 +15,12 @@ use thiserror::Error; #[error("{0}")] pub struct CommandOutputError(String); +const PFEXEC: &str = "/usr/bin/pfexec"; +const SVCPROP: &str = "/usr/bin/svcprop"; +const SVCS: &str = "/usr/bin/svcs"; +const SVCCFG: &str = "/usr/sbin/svccfg"; +const SVCADM: &str = "/usr/sbin/svcadm"; + trait OutputExt { fn read_stdout(&self) -> Result; } @@ -302,21 +309,22 @@ impl Query { } // Issues command, returns stdout. - fn issue_command(&self, args: Vec) -> Result { - Ok(std::process::Command::new("/usr/bin/svcs") + fn run(&self, args: Vec) -> Result { + Ok(std::process::Command::new(PFEXEC) .env_clear() + .arg(SVCS) .args(args) .output() .map_err(QueryError::Command)? .read_stdout()?) } - fn issue_status_command( + fn run_and_parse_output( &self, args: Vec, ) -> Result, QueryError> { Ok(self - .issue_command(args)? + .run(args)? .split('\n') .map(|s| s.parse::()) .collect::, _>>()? @@ -380,7 +388,7 @@ impl Query { QuerySelection::ByPattern(names) => self.add_patterns(&mut args, names), } - self.issue_status_command(args) + self.run_and_parse_output(args) } // Shared implementation for getting dependencies and dependents. @@ -399,7 +407,7 @@ impl Query { // XXX patterns need cleaning, same in other getters self.add_patterns(&mut args, patterns); - self.issue_status_command(args) + self.run_and_parse_output(args) } /// Returns the statuses of service instances upon which the provided @@ -466,7 +474,7 @@ impl Query { self.add_zone_to_args(&mut args); self.add_patterns(&mut args, patterns); Ok(self - .issue_command(args)? + .run(args)? .split('\n') .map(|s| s.parse::()) .collect::, _>>() @@ -612,17 +620,24 @@ impl ConfigExport { self } - /// Runs the export command, returning the manifest output as a string. - pub fn run>(&mut self, fmri: S) -> Result { + /// Returns the export command without running it. + pub fn as_command>(&mut self, fmri: S) -> Command { let mut args = vec!["export"]; if self.archive { args.push("-a"); } args.push(fmri.as_ref()); - Ok(std::process::Command::new("/usr/sbin/svccfg") - .env_clear() - .args(args) + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear() + .arg(SVCCFG) + .args(args); + cmd + } + + /// Runs the export command, returning the manifest output as a string. + pub fn run>(&mut self, fmri: S) -> Result { + Ok(self.as_command(fmri) .output() .map_err(ConfigError::Command)? .read_stdout()?) @@ -647,8 +662,8 @@ impl ConfigImport { self } - /// Runs the import command. - pub fn run>(&mut self, path: P) -> Result<(), ConfigError> { + /// Returns the import command, without running it. + pub fn as_command>(&mut self, path: P) -> Command { let mut args = vec!["import"]; if self.validate { args.push("-V"); @@ -656,9 +671,16 @@ impl ConfigImport { let path_str = path.as_ref().to_string_lossy(); args.push(&path_str); - std::process::Command::new("/usr/sbin/svccfg") - .env_clear() - .args(args) + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear() + .arg(SVCCFG) + .args(args); + cmd + } + + /// Runs the import command. + pub fn run>(&mut self, path: P) -> Result<(), ConfigError> { + self.as_command(path) .output() .map_err(ConfigError::Command)? .read_stdout() @@ -686,17 +708,24 @@ impl ConfigDelete { self } - /// Runs the deletion command. - pub fn run>(&mut self, fmri: S) -> Result<(), ConfigError> { + /// Returns the deletion command, without running it. + pub fn as_command>(&mut self, fmri: S) -> Command { let mut args = vec!["delete"]; if self.force { args.push("-f"); } args.push(fmri.as_ref()); - std::process::Command::new("/usr/sbin/svccfg") - .env_clear() - .args(args) + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear() + .arg(SVCCFG) + .args(args); + cmd + } + + /// Runs the deletion command. + pub fn run>(&mut self, fmri: S) -> Result<(), ConfigError> { + self.as_command(fmri) .output() .map_err(ConfigError::Command)? .read_stdout() @@ -715,12 +744,19 @@ impl ConfigAdd { ConfigAdd { fmri } } + /// Returns the command, without running it + pub fn as_command>(&mut self, child: S) -> Command { + let args = vec!["-s", &self.fmri, "add", child.as_ref()]; + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear() + .arg(SVCCFG) + .args(args); + cmd + } + /// Runs the add entity command. pub fn run>(&mut self, child: S) -> Result<(), ConfigError> { - let args = vec!["-s", &self.fmri, "add", child.as_ref()]; - std::process::Command::new("/usr/sbin/svccfg") - .env_clear() - .args(args) + self.as_command(child) .output() .map_err(ConfigError::Command)? .read_stdout() @@ -739,8 +775,8 @@ impl ConfigSetProperty { ConfigSetProperty { fmri } } - /// Runs the set property command. - pub fn run(&self, property: Property) -> Result<(), ConfigError> { + /// Returns the command which would set a property + pub fn as_command(&self, property: Property) -> Command { let prop = format!( "{} = {}", property.name.to_string(), @@ -748,9 +784,17 @@ impl ConfigSetProperty { ); let args = vec!["-s", &self.fmri, "setprop", &prop]; - std::process::Command::new("/usr/sbin/svccfg") + let mut cmd = std::process::Command::new(PFEXEC); + cmd .env_clear() - .args(args) + .arg(SVCCFG) + .args(args); + cmd + } + + /// Runs the command to set a property + pub fn run(&self, property: Property) -> Result<(), ConfigError> { + self.as_command(property) .output() .map_err(ConfigError::Command)? .read_stdout() @@ -770,14 +814,21 @@ impl ConfigAddPropertyValue { Self { fmri } } - /// Runs the add property value command. - pub fn run(&self, property: Property) -> Result<(), ConfigError> { + /// Returns the command to add a property + pub fn as_command(&self, property: Property) -> Command { let name = property.name.to_string(); let value = property.value.to_string(); let args = vec!["-s", &self.fmri, "addpropvalue", &name, &value]; - std::process::Command::new("/usr/sbin/svccfg") - .env_clear() - .args(args) + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear() + .arg(SVCCFG) + .args(args); + cmd + } + + /// Runs the add property value command. + pub fn run(&self, property: Property) -> Result<(), ConfigError> { + self.as_command(property) .output() .map_err(ConfigError::Command)? .read_stdout() @@ -797,13 +848,21 @@ impl ConfigDeletePropertyValue { Self { fmri } } - /// Runs the delete property value command. - pub fn run(&self, property_name: &PropertyName, value: &str) -> Result<(), ConfigError> { + /// Returns the command without running it. + pub fn as_command(&self, property_name: &PropertyName, value: &str) -> Command { let name = property_name.to_string(); let args = vec!["-s", &self.fmri, "delpropvalue", &name, value]; - std::process::Command::new("/usr/sbin/svccfg") + let mut cmd = std::process::Command::new(PFEXEC); + cmd .env_clear() - .args(args) + .arg(SVCCFG) + .args(args); + cmd + } + + /// Runs the delete property value command. + pub fn run(&self, property_name: &PropertyName, value: &str) -> Result<(), ConfigError> { + self.as_command(property_name, value) .output() .map_err(ConfigError::Command)? .read_stdout() @@ -876,16 +935,6 @@ impl Adm { self } - fn run(args: Vec) -> Result<(), AdmError> { - std::process::Command::new("/usr/sbin/svcadm") - .env_clear() - .args(args) - .output() - .map_err(AdmError::Command)? - .read_stdout()?; - Ok(()) - } - /// Builds a [AdmEnable] object. /// /// ```no_run @@ -975,10 +1024,10 @@ trait AdmSubcommand { } /// Shared mechanism of running all subcommands created by [Adm]. -fn run_adm_subcommand( +fn as_adm_subcommand( subcommand: &C, selection: AdmSelection, -) -> Result<(), AdmError> +) -> Command where C: AdmSubcommand, S: AsRef, @@ -1001,7 +1050,29 @@ where args.extend(pattern.into_iter().map(|s| s.as_ref().to_string())); } } - Adm::run(args) + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear() + .arg(SVCADM) + .args(args); + cmd +} + + +/// Shared mechanism of running all subcommands created by [Adm]. +fn run_adm_subcommand( + subcommand: &C, + selection: AdmSelection, +) -> Result<(), AdmError> +where + C: AdmSubcommand, + S: AsRef, + I: IntoIterator, +{ + as_adm_subcommand(subcommand, selection) + .output() + .map_err(AdmError::Command)? + .read_stdout()?; + Ok(()) } /// Created by [Adm::enable], enables the service instance(s). @@ -1063,6 +1134,15 @@ impl<'a> AdmEnable<'a> { self } + /// Returns the command, without running it + pub fn as_command(&mut self, selection: AdmSelection) -> Command + where + S: AsRef, + I: IntoIterator, + { + as_adm_subcommand(self, selection) + } + /// Runs the command. pub fn run(&mut self, selection: AdmSelection) -> Result<(), AdmError> where @@ -1132,6 +1212,15 @@ impl<'a> AdmDisable<'a> { self } + /// Returns the command, without running it + pub fn as_command(&mut self, selection: AdmSelection) -> Command + where + S: AsRef, + I: IntoIterator, + { + as_adm_subcommand(self, selection) + } + /// Runs the command. pub fn run(&mut self, selection: AdmSelection) -> Result<(), AdmError> where @@ -1173,6 +1262,15 @@ impl<'a> AdmRestart<'a> { self } + /// Returns the command, without running it + pub fn as_command(&mut self, selection: AdmSelection) -> Command + where + S: AsRef, + I: IntoIterator, + { + as_adm_subcommand(self, selection) + } + /// Runs the command. pub fn run(&mut self, selection: AdmSelection) -> Result<(), AdmError> where @@ -1206,6 +1304,15 @@ impl<'a> AdmRefresh<'a> { Self { adm } } + /// Returns the command, without running it + pub fn as_command(&mut self, selection: AdmSelection) -> Command + where + S: AsRef, + I: IntoIterator, + { + as_adm_subcommand(self, selection) + } + /// Runs the command. pub fn run(&mut self, selection: AdmSelection) -> Result<(), AdmError> where @@ -1240,6 +1347,15 @@ impl<'a> AdmClear<'a> { Self { adm } } + /// Returns the command, without running it + pub fn as_command(&mut self, selection: AdmSelection) -> Command + where + S: AsRef, + I: IntoIterator, + { + as_adm_subcommand(self, selection) + } + /// Runs the command. pub fn run(&mut self, selection: AdmSelection) -> Result<(), AdmError> where @@ -1624,8 +1740,8 @@ impl<'a> PropertyLookup<'a> { } } - /// Looks up a property for a specified FMRI. - pub fn run(&mut self, property: &PropertyName, fmri: S) -> Result + /// Returns the command to lookup a property + pub fn as_command(&mut self, property: &PropertyName, fmri: S) -> Command where S: AsRef, { @@ -1649,15 +1765,30 @@ impl<'a> PropertyLookup<'a> { // Requests a single FMRI. args.push(fmri.as_ref().into()); - let out = std::process::Command::new("/usr/bin/svcprop") + let mut cmd = std::process::Command::new(PFEXEC); + cmd .env_clear() - .args(args) - .output() - .map_err(PropertyError::Command)? - .read_stdout()?; + .arg(SVCPROP) + .args(args); + cmd + } + /// Parses the output for an executed command from [Self::as_command]. + pub fn parse_output(output: &Output) -> Result { + let out = output.read_stdout()?; out.parse().map_err(|err: PropertyParseError| err.into()) } + + /// Looks up a property for a specified FMRI. + pub fn run(&mut self, property: &PropertyName, fmri: S) -> Result + where + S: AsRef, + { + let out = self.as_command(property, fmri) + .output() + .map_err(PropertyError::Command)?; + Self::parse_output(&out) + } } /// Created by [Properties::wait], a builder object waiting for a property group to change. @@ -1670,15 +1801,12 @@ impl<'a> PropertyWait<'a> { PropertyWait { property_base } } - /// Waits until a specified property group changes before printing. - /// - /// Returns requested property - note that it might not be the - /// property which changed. - pub fn run( + /// Returns the Command to wait for a property, without running it. + pub fn as_command( &mut self, property: &PropertyGroupName, fmri: S, - ) -> Result + ) -> Command where S: AsRef, { @@ -1702,15 +1830,36 @@ impl<'a> PropertyWait<'a> { // Requests a single FMRI. args.push(fmri.as_ref().into()); - let out = std::process::Command::new("/usr/bin/svcprop") - .env_clear() - .args(args) - .output() - .map_err(PropertyError::Command)? - .read_stdout()?; + let mut cmd = std::process::Command::new(PFEXEC); + cmd.env_clear() + .arg(SVCPROP) + .args(args); + cmd + } + /// Parses the output for an executed command from [Self::as_command]. + pub fn parse_output(output: &Output) -> Result { + let out = output.read_stdout()?; out.parse().map_err(|err: PropertyParseError| err.into()) } + + /// Waits until a specified property group changes before printing. + /// + /// Returns requested property - note that it might not be the + /// property which changed. + pub fn run( + &mut self, + property: &PropertyGroupName, + fmri: S, + ) -> Result + where + S: AsRef, + { + let output = self.as_command(property, fmri) + .output() + .map_err(PropertyError::Command)?; + Self::parse_output(&output) + } } #[cfg(test)] From e054402d3613317cb4eac1ddb7c51f6693300f31 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Thu, 10 Aug 2023 13:19:40 -0700 Subject: [PATCH 2/3] fmt --- src/smf.rs | 64 +++++++++++++++--------------------------------------- 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/src/smf.rs b/src/smf.rs index 50ab5bb..a0f44a3 100644 --- a/src/smf.rs +++ b/src/smf.rs @@ -629,15 +629,14 @@ impl ConfigExport { args.push(fmri.as_ref()); let mut cmd = std::process::Command::new(PFEXEC); - cmd.env_clear() - .arg(SVCCFG) - .args(args); + cmd.env_clear().arg(SVCCFG).args(args); cmd } /// Runs the export command, returning the manifest output as a string. pub fn run>(&mut self, fmri: S) -> Result { - Ok(self.as_command(fmri) + Ok(self + .as_command(fmri) .output() .map_err(ConfigError::Command)? .read_stdout()?) @@ -672,9 +671,7 @@ impl ConfigImport { args.push(&path_str); let mut cmd = std::process::Command::new(PFEXEC); - cmd.env_clear() - .arg(SVCCFG) - .args(args); + cmd.env_clear().arg(SVCCFG).args(args); cmd } @@ -717,9 +714,7 @@ impl ConfigDelete { args.push(fmri.as_ref()); let mut cmd = std::process::Command::new(PFEXEC); - cmd.env_clear() - .arg(SVCCFG) - .args(args); + cmd.env_clear().arg(SVCCFG).args(args); cmd } @@ -748,9 +743,7 @@ impl ConfigAdd { pub fn as_command>(&mut self, child: S) -> Command { let args = vec!["-s", &self.fmri, "add", child.as_ref()]; let mut cmd = std::process::Command::new(PFEXEC); - cmd.env_clear() - .arg(SVCCFG) - .args(args); + cmd.env_clear().arg(SVCCFG).args(args); cmd } @@ -785,10 +778,7 @@ impl ConfigSetProperty { let args = vec!["-s", &self.fmri, "setprop", &prop]; let mut cmd = std::process::Command::new(PFEXEC); - cmd - .env_clear() - .arg(SVCCFG) - .args(args); + cmd.env_clear().arg(SVCCFG).args(args); cmd } @@ -820,9 +810,7 @@ impl ConfigAddPropertyValue { let value = property.value.to_string(); let args = vec!["-s", &self.fmri, "addpropvalue", &name, &value]; let mut cmd = std::process::Command::new(PFEXEC); - cmd.env_clear() - .arg(SVCCFG) - .args(args); + cmd.env_clear().arg(SVCCFG).args(args); cmd } @@ -853,10 +841,7 @@ impl ConfigDeletePropertyValue { let name = property_name.to_string(); let args = vec!["-s", &self.fmri, "delpropvalue", &name, value]; let mut cmd = std::process::Command::new(PFEXEC); - cmd - .env_clear() - .arg(SVCCFG) - .args(args); + cmd.env_clear().arg(SVCCFG).args(args); cmd } @@ -1024,10 +1009,7 @@ trait AdmSubcommand { } /// Shared mechanism of running all subcommands created by [Adm]. -fn as_adm_subcommand( - subcommand: &C, - selection: AdmSelection, -) -> Command +fn as_adm_subcommand(subcommand: &C, selection: AdmSelection) -> Command where C: AdmSubcommand, S: AsRef, @@ -1051,13 +1033,10 @@ where } } let mut cmd = std::process::Command::new(PFEXEC); - cmd.env_clear() - .arg(SVCADM) - .args(args); + cmd.env_clear().arg(SVCADM).args(args); cmd } - /// Shared mechanism of running all subcommands created by [Adm]. fn run_adm_subcommand( subcommand: &C, @@ -1766,10 +1745,7 @@ impl<'a> PropertyLookup<'a> { args.push(fmri.as_ref().into()); let mut cmd = std::process::Command::new(PFEXEC); - cmd - .env_clear() - .arg(SVCPROP) - .args(args); + cmd.env_clear().arg(SVCPROP).args(args); cmd } @@ -1784,7 +1760,8 @@ impl<'a> PropertyLookup<'a> { where S: AsRef, { - let out = self.as_command(property, fmri) + let out = self + .as_command(property, fmri) .output() .map_err(PropertyError::Command)?; Self::parse_output(&out) @@ -1802,11 +1779,7 @@ impl<'a> PropertyWait<'a> { } /// Returns the Command to wait for a property, without running it. - pub fn as_command( - &mut self, - property: &PropertyGroupName, - fmri: S, - ) -> Command + pub fn as_command(&mut self, property: &PropertyGroupName, fmri: S) -> Command where S: AsRef, { @@ -1831,9 +1804,7 @@ impl<'a> PropertyWait<'a> { args.push(fmri.as_ref().into()); let mut cmd = std::process::Command::new(PFEXEC); - cmd.env_clear() - .arg(SVCPROP) - .args(args); + cmd.env_clear().arg(SVCPROP).args(args); cmd } @@ -1855,7 +1826,8 @@ impl<'a> PropertyWait<'a> { where S: AsRef, { - let output = self.as_command(property, fmri) + let output = self + .as_command(property, fmri) .output() .map_err(PropertyError::Command)?; Self::parse_output(&output) From 5e3853a3830aaa6192284ed7d1b7190c794d98f4 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Thu, 10 Aug 2023 13:24:59 -0700 Subject: [PATCH 3/3] Bump rev --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2fe43c2..511b070 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smf" -version = "0.2.1" +version = "0.2.2" authors = ["Sean Klein "] edition = "2018" repository = "https://github.com/oxidecomputer/smf"