use secafs_sdk::{SecAFSOptions, FileSystem, HostFS, OverlayFS};
use anyhow::{Context, Result};
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
use tokio::sync::Mutex;
use secafs_sdk::db::DbValue as Value;
use crate::cmd::init::open_secafs;
use crate::mount::{mount_fs, MountBackend, MountOpts};
pub async fn handle_exec_command(
postgres_url: String,
command: PathBuf,
args: Vec<String>,
backend: MountBackend,
) -> Result<()> {
let opts = SecAFSOptions::resolve(&postgres_url)?;
let secafs_inst = open_secafs(opts).await?;
let fs: Arc<Mutex<dyn FileSystem + Send>> = {
let conn = secafs_inst.get_connection().await?;
let query = "SELECT value FROM fs_overlay_config WHERE key = 'base_path'";
let base_path: Option<String> = match conn.query(query, ()).await {
Ok(mut rows) => {
if let Ok(Some(row)) = rows.next().await {
row.get_value(0).ok().and_then(|v| {
if let Value::Text(s) = v {
Some(s.clone())
} else {
None
}
})
} else {
None
}
}
Err(_) => None,
};
if let Some(base_path) = base_path {
eprintln!("Using overlay filesystem with base: {}", base_path);
let hostfs = HostFS::new(&base_path)?;
let overlay = OverlayFS::new(Arc::new(hostfs), secafs_inst.fs);
overlay.load().await?;
Arc::new(Mutex::new(overlay)) as Arc<Mutex<dyn FileSystem + Send>>
} else {
Arc::new(Mutex::new(secafs_inst.fs)) as Arc<Mutex<dyn FileSystem + Send>>
}
};
let exec_id = uuid::Uuid::new_v4().to_string();
let mountpoint = std::env::temp_dir().join(format!("secafs-exec-{}", exec_id));
std::fs::create_dir_all(&mountpoint).context("Failed to create mount directory")?;
let fsname = format!("secafs:{}", postgres_url);
let mount_opts = MountOpts {
mountpoint: mountpoint.clone(),
backend,
fsname,
uid: None,
gid: None,
allow_other: false,
allow_root: false,
auto_unmount: false,
lazy_unmount: true,
timeout: std::time::Duration::from_secs(10),
};
let _mount_handle = mount_fs(fs, mount_opts).await?;
let status = Command::new(&command)
.args(&args)
.current_dir(&mountpoint)
.status()
.with_context(|| format!("Failed to execute: {}", command.display()))?;
drop(_mount_handle);
let _ = std::fs::remove_dir_all(&mountpoint);
if !status.success() {
std::process::exit(status.code().unwrap_or(1));
}
Ok(())
}