use std::collections::HashSet;
use std::path::Path;
use std::process::Command;
use anyhow::{bail, Context, Result};
use openat_ext::OpenatDirExt;
#[allow(dead_code)]
pub(crate) fn getenv_utf8(n: &str) -> Result<Option<String>> {
if let Some(v) = std::env::var_os(n) {
Ok(Some(
v.to_str()
.ok_or_else(|| anyhow::anyhow!("{} is invalid UTF-8", n))?
.to_string(),
))
} else {
Ok(None)
}
}
pub(crate) fn filenames(dir: &openat::Dir) -> Result<HashSet<String>> {
let mut ret = HashSet::new();
for entry in dir.list_dir(".")? {
let entry = entry?;
let Some(name) = entry.file_name().to_str() else {
bail!("Invalid UTF-8 filename: {:?}", entry.file_name())
};
match dir.get_file_type(&entry)? {
openat::SimpleType::File => {
ret.insert(format!("/{name}"));
}
openat::SimpleType::Dir => {
let child = dir.sub_dir(name)?;
for mut k in filenames(&child)?.drain() {
k.reserve(name.len() + 1);
k.insert_str(0, name);
k.insert(0, '/');
ret.insert(k);
}
}
openat::SimpleType::Symlink => {
bail!("Unsupported symbolic link {:?}", entry.file_name())
}
openat::SimpleType::Other => {
bail!("Unsupported non-file/directory {:?}", entry.file_name())
}
}
}
Ok(ret)
}
pub(crate) fn ensure_writable_mount<P: AsRef<Path>>(p: P) -> Result<()> {
let p = p.as_ref();
let stat = rustix::fs::statvfs(p)?;
if !stat.f_flag.contains(rustix::fs::StatVfsMountFlags::RDONLY) {
return Ok(());
}
let status = std::process::Command::new("mount")
.args(["-o", "remount,rw"])
.arg(p)
.status()?;
if !status.success() {
anyhow::bail!("Failed to remount {:?} writable", p);
}
Ok(())
}
#[allow(dead_code)]
pub(crate) fn cmd_output(cmd: &mut Command) -> Result<String> {
let result = cmd
.output()
.with_context(|| format!("running {:#?}", cmd))?;
if !result.status.success() {
eprintln!("{}", String::from_utf8_lossy(&result.stderr));
bail!("{:#?} failed with {}", cmd, result.status);
}
String::from_utf8(result.stdout)
.with_context(|| format!("decoding as UTF-8 output of `{:#?}`", cmd))
}
pub fn running_in_container() -> bool {
if std::env::var_os("container").is_some() {
return true;
}
for p in ["/run/.containerenv", "/.dockerenv"] {
if Path::new(p).exists() {
return true;
}
}
false
}
#[derive(Debug)]
pub(crate) struct SignalTerminationGuard(signal_hook_registry::SigId);
impl SignalTerminationGuard {
pub(crate) fn new() -> Result<Self> {
let signal = unsafe { signal_hook_registry::register(libc::SIGTERM, || {})? };
Ok(Self(signal))
}
}
impl Drop for SignalTerminationGuard {
fn drop(&mut self) {
signal_hook_registry::unregister(self.0);
}
}