Added macos client
This commit is contained in:
125
furumi-mount-macos/src/main.rs
Normal file
125
furumi-mount-macos/src/main.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
pub mod nfs;
|
||||
|
||||
use clap::Parser;
|
||||
use furumi_client_core::FurumiClient;
|
||||
use nfsserve::tcp::{NFSTcp, NFSTcpListener};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about = "Furumi-ng: mount remote filesystem via encrypted gRPC + NFS (macOS)")]
|
||||
struct Args {
|
||||
/// Server address (IP:PORT, will use https:// by default unless --no-tls is specified)
|
||||
#[arg(short, long, env = "FURUMI_SERVER", default_value = "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,
|
||||
|
||||
/// Disable TLS encryption
|
||||
#[arg(long, default_value_t = false)]
|
||||
no_tls: bool,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?;
|
||||
|
||||
let full_addr = if args.no_tls {
|
||||
if args.server.starts_with("http://") || args.server.starts_with("https://") {
|
||||
args.server.clone()
|
||||
} else {
|
||||
format!("http://{}", args.server)
|
||||
}
|
||||
} else if args.server.starts_with("http://") || args.server.starts_with("https://") {
|
||||
args.server.clone()
|
||||
} else {
|
||||
format!("https://{}", args.server)
|
||||
};
|
||||
|
||||
let client = rt.block_on(async { FurumiClient::connect(&full_addr, &args.token).await })?;
|
||||
|
||||
let furumi_nfs = nfs::FurumiNfs::new(client);
|
||||
|
||||
let mount_point = args.mount.clone();
|
||||
let mount_point_umount = args.mount.clone();
|
||||
|
||||
// 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);
|
||||
})?;
|
||||
|
||||
rt.block_on(async move {
|
||||
// Bind NFS server to a random available port on localhost
|
||||
let listener = NFSTcpListener::bind("127.0.0.1:0", furumi_nfs)
|
||||
.await
|
||||
.expect("Failed to bind NFS listener");
|
||||
|
||||
let port = listener.get_listen_port();
|
||||
info!("NFS server listening on 127.0.0.1:{}", port);
|
||||
|
||||
// Start handling NFS connections BEFORE mounting —
|
||||
// mount_nfs needs to talk to the server during mount.
|
||||
let handle = tokio::spawn(async move {
|
||||
if let Err(e) = listener.handle_forever().await {
|
||||
eprintln!("NFS server error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Mount via macOS native mount_nfs
|
||||
let mount_path = mount_point.to_string_lossy().to_string();
|
||||
let opts = format!(
|
||||
"rdonly,locallocks,noresvport,nfsvers=3,tcp,rsize=1048576,port={},mountport={}",
|
||||
port, port
|
||||
);
|
||||
|
||||
let status = Command::new("mount_nfs")
|
||||
.args(["-o", &opts, "127.0.0.1:/", &mount_path])
|
||||
.status()
|
||||
.expect("Failed to execute mount_nfs");
|
||||
|
||||
if !status.success() {
|
||||
handle.abort();
|
||||
eprintln!("Error: mount_nfs failed with status {}", status);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
println!("Mounted Furumi-ng to {:?}", mount_path);
|
||||
|
||||
// Wait for shutdown signal
|
||||
while running.load(Ordering::SeqCst) {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
|
||||
// Unmount
|
||||
let _ = Command::new("umount").arg(mount_point_umount.to_string_lossy().as_ref()).status();
|
||||
handle.abort();
|
||||
println!("Unmounted successfully.");
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user