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)] #[command(version, about = "Furumi-ng: mount remote filesystem via encrypted gRPC + FUSE")] struct Args { /// Server address (use https:// for encrypted, http:// for plaintext) #[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, } fn main() -> Result<(), Box> { 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); } // 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 { FurumiClient::connect(&args.server, &args.token).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(()) }