use std::path::{Path, PathBuf};
#[cfg(unix)]
use std::process::Stdio;
use bestool_kopia::{SnapshotSelectorArgs, short_id};
use clap::Parser;
use miette::{Context as _, IntoDiagnostic as _, Result};
#[cfg(not(unix))]
use miette::bail;
use super::{
KopiaArgs,
common::{current_hostname, kopia_binary},
};
use crate::actions::Context;
#[derive(Debug, Clone, Parser)]
pub struct MountArgs {
#[arg(value_name = "MOUNTPOINT")]
pub mountpoint: PathBuf,
#[command(flatten)]
pub selector: SnapshotSelectorArgs,
#[arg(long)]
pub background: bool,
}
pub async fn run(args: MountArgs, ctx: Context) -> Result<()> {
let kopia = ctx.require::<KopiaArgs>();
let bin = kopia_binary(kopia)?;
let chosen = args
.selector
.resolve(&bin, current_hostname(), "Choose a snapshot to mount")?;
if args.background {
mount_background(&bin, &chosen.id, &args.mountpoint)
} else {
println!(
"Mounting snapshot {} at {} (Ctrl+C to unmount)",
short_id(&chosen.id),
args.mountpoint.display()
);
mount_foreground(&bin, &chosen.id, &args.mountpoint)
}
}
fn mount_foreground(bin: &Path, snapshot_id: &str, mountpoint: &Path) -> Result<()> {
let status = std::process::Command::new(bin)
.arg("mount")
.arg(snapshot_id)
.arg(mountpoint)
.env("KOPIA_CHECK_FOR_UPDATES", "false")
.status()
.into_diagnostic()
.wrap_err_with(|| format!("invoking {}", bin.display()))?;
if !status.success() {
std::process::exit(status.code().unwrap_or(1));
}
Ok(())
}
#[cfg(unix)]
fn mount_background(bin: &Path, snapshot_id: &str, mountpoint: &Path) -> Result<()> {
let child = std::process::Command::new("setsid")
.arg("--fork")
.arg(bin)
.arg("mount")
.arg(snapshot_id)
.arg(mountpoint)
.env("KOPIA_CHECK_FOR_UPDATES", "false")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.into_diagnostic()
.wrap_err("spawning `setsid --fork`; install util-linux if missing")?;
println!(
"Mounted snapshot {} at {} in the background; the kopia process detached via setsid (parent setsid pid {})",
short_id(snapshot_id),
mountpoint.display(),
child.id(),
);
Ok(())
}
#[cfg(not(unix))]
fn mount_background(_bin: &Path, _snapshot_id: &str, _mountpoint: &Path) -> Result<()> {
bail!("--background mounts are only supported on Unix");
}