#![cfg(unix)]
use std::path::Path;
use tokio::process::Command;
use tracing::debug;
pub async fn capture(program: &str, args: &[&str]) -> Option<String> {
let output = Command::new(program)
.args(args)
.stdin(std::process::Stdio::null())
.output()
.await
.inspect_err(|err| debug!("could not run {program}: {err}"))
.ok()?;
if !output.status.success() {
debug!("{program} {} exited {}", args.join(" "), output.status);
return None;
}
let mut text = match String::from_utf8(output.stdout) {
Ok(text) => text,
Err(err) => String::from_utf8_lossy(err.as_bytes()).into_owned(),
};
text.truncate(text.trim_end().len());
let lead = text.len() - text.trim_start().len();
text.drain(..lead);
Some(text)
}
pub async fn run(program: &str, args: &[&str]) -> Option<()> {
capture(program, args).await.map(|_| ())
}
pub async fn findmnt(field: &str, path: &Path) -> Option<String> {
capture(
"findmnt",
&["-n", "-o", field, "--target", "--", &path.to_string_lossy()],
)
.await
.filter(|value| !value.is_empty())
}
pub async fn lvs(field: &str, target: &str, bytes: bool) -> Option<String> {
let mut args = vec!["--noheadings", "-o", field];
if bytes {
args.extend(["--units", "b", "--nosuffix"]);
}
args.extend(["--", target]);
capture("lvs", &args).await.filter(|value| !value.is_empty())
}
#[derive(Debug, Clone)]
pub struct ThinPool {
pub vg: String,
pub pool: String,
}
impl ThinPool {
pub fn qualified(&self) -> String {
format!("{}/{}", self.vg, self.pool)
}
}
pub async fn thin_pool_of(vg: &str, lv: &str) -> Option<ThinPool> {
let pool = lvs("pool_lv", &format!("{vg}/{lv}"), false).await?;
Some(ThinPool {
vg: vg.to_owned(),
pool,
})
}
pub fn mangle(name: &str) -> String {
name.replace('-', "--")
}
pub fn dm_name(vg: &str, lv: &str) -> String {
format!("{}-{}", mangle(vg), mangle(lv))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_hyphen_in_a_name_is_doubled() {
assert_eq!(mangle("vg-data"), "vg--data");
assert_eq!(mangle("vg0"), "vg0");
}
#[test]
fn mangling_keeps_two_different_devices_apart() {
assert_ne!(dm_name("vg", "data-pool"), dm_name("vg-data", "pool"));
assert_eq!(dm_name("vg", "data-pool"), "vg-data--pool");
assert_eq!(dm_name("vg-data", "pool"), "vg--data-pool");
}
}