Files
furumi-ng/furumi-mount-linux/src/main.rs

96 lines
3.0 KiB
Rust
Raw Normal View History

pub mod fs;
use clap::Parser;
use fuser::{MountOption, Session};
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use furumi_client_core::FurumiClient;
#[derive(Parser, Debug)]
2026-03-10 16:20:19 +00:00
#[command(version, about = "Furumi-ng: mount remote filesystem via encrypted gRPC + FUSE")]
struct Args {
2026-03-10 16:20:19 +00:00
/// Server address to connect to (use https:// for encrypted connection)
#[arg(short, long, env = "FURUMI_SERVER", default_value = "https://0.0.0.0:50051")]
server: String,
/// Authentication Bearer token (leave empty if auth is disabled on server)
#[arg(short, long, env = "FURUMI_TOKEN", default_value = "")]
token: String,
/// Mount point directory
#[arg(short, long, env = "FURUMI_MOUNT")]
mount: PathBuf,
2026-03-10 16:20:19 +00:00
/// Path to server's TLS CA certificate PEM file (required for https:// connections)
#[arg(long, env = "FURUMI_TLS_CA")]
tls_ca: Option<PathBuf>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let args = Args::parse();
if !args.mount.exists() || !args.mount.is_dir() {
eprintln!("Error: Mount point {:?} does not exist or is not a directory", args.mount);
std::process::exit(1);
}
2026-03-10 16:20:19 +00:00
// Load TLS CA certificate if provided
let tls_ca_pem = if let Some(ref ca_path) = args.tls_ca {
let pem = std::fs::read(ca_path).unwrap_or_else(|e| {
eprintln!("Error: Failed to read TLS CA cert {:?}: {}", ca_path, e);
std::process::exit(1);
});
Some(pem)
} else {
None
};
// Create a robust tokio runtime for the background gRPC work
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
let client = rt.block_on(async {
2026-03-10 16:20:19 +00:00
FurumiClient::connect(&args.server, &args.token, tls_ca_pem).await
})?;
let fuse_fs = fs::FurumiFuse::new(client, rt.handle().clone());
let options = vec![
MountOption::RO,
MountOption::FSName("furumi-ng".to_string()),
MountOption::NoExec, // Better security for media mount
];
println!("Mounting Furumi-ng to {:?}", args.mount);
// Use Session + BackgroundSession for graceful unmount on exit
let session = Session::new(fuse_fs, &args.mount, &options)?;
// Spawn the FUSE event loop in a background thread.
// When `bg_session` is dropped, the filesystem is automatically unmounted.
let bg_session = session.spawn()?;
// Set up signal handler for graceful shutdown
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
ctrlc::set_handler(move || {
eprintln!("\nReceived signal, unmounting...");
r.store(false, Ordering::SeqCst);
})?;
// Wait for shutdown signal
while running.load(Ordering::SeqCst) {
std::thread::sleep(std::time::Duration::from_millis(100));
}
// Dropping bg_session automatically unmounts the filesystem
drop(bg_session);
println!("Unmounted successfully.");
Ok(())
}