aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client.rs58
-rw-r--r--src/main.rs76
-rw-r--r--src/server.rs8
3 files changed, 123 insertions, 19 deletions
diff --git a/src/client.rs b/src/client.rs
index 492008a..0f86712 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,6 +1,7 @@
use anyhow::{anyhow, bail, Result};
+use log::debug;
use std::{
- io::{BufRead, BufReader, BufWriter, Write},
+ io::{copy, BufRead, BufReader, BufWriter, Read, Write},
net::{TcpStream, ToSocketAddrs},
};
@@ -25,8 +26,9 @@ impl Client {
let mut line = String::new();
let mut out = Vec::new();
loop {
+ line.clear();
self.rsock.read_line(&mut line)?;
- if line.trim().is_empty() || line.ends_with("\n\n") {
+ if line.trim().is_empty() {
break Ok(out);
}
check_error(&line)?;
@@ -35,6 +37,56 @@ impl Client {
out.push((mtime.parse()?, size.parse()?, serial.parse()?));
}
}
+ pub fn upload(&mut self, size: u64, mut reader: impl Read) -> Result<()> {
+ debug!("announcing upload of {size} bytes");
+ writeln!(self.wsock, "upload,{size}")?;
+ self.wsock.flush()?;
+
+ let mut line = String::new();
+ self.rsock.read_line(&mut line)?;
+ check_error(&line)?;
+ if line.trim() != "ready" {
+ bail!("invalid response, not ready");
+ }
+ debug!("server ready");
+
+ copy(&mut reader, &mut self.wsock)?;
+
+ line.clear();
+ self.rsock.read_line(&mut line)?;
+ check_error(&line)?;
+ if line.trim() != "done" {
+ bail!("invalid response, not done");
+ }
+ debug!("server done");
+ Ok(())
+ }
+ pub fn download(&mut self, serial: Serial, mut writer: impl Write) -> Result<()> {
+ debug!("requesting download for serial={serial}");
+ writeln!(self.wsock, "download,{serial}")?;
+ self.wsock.flush()?;
+
+ let mut line = String::new();
+ self.rsock.read_line(&mut line)?;
+ check_error(&line)?;
+ let Some(size) = line.trim().strip_prefix("ready,") else {
+ bail!("server not ready")
+ };
+ eprintln!("{size:?}");
+ let size = size.parse()?;
+ debug!("server ready");
+
+ copy(&mut self.rsock.by_ref().take(size), &mut writer)?;
+
+ line.clear();
+ self.rsock.read_line(&mut line)?;
+ check_error(&line)?;
+ if line.trim() != "done" {
+ bail!("invalid response, not done");
+ }
+ debug!("server done");
+ Ok(())
+ }
pub fn quit(mut self) -> Result<()> {
writeln!(self.wsock, "quit")?;
self.wsock.flush()?;
@@ -43,7 +95,7 @@ impl Client {
}
fn check_error(line: &str) -> Result<()> {
- if let Some(message) = line.trim().strip_prefix("error") {
+ if let Some(message) = line.trim().strip_prefix("error,") {
bail!("server error: {message}")
}
Ok(())
diff --git a/src/main.rs b/src/main.rs
index c72aad7..22977ae 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,12 +2,18 @@
pub mod client;
pub mod server;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use clap::{Parser, Subcommand};
use client::Client;
+use log::info;
use serde::Deserialize;
use server::server;
-use std::{fs::read_to_string, net::SocketAddr, path::PathBuf};
+use std::{
+ fs::{read_to_string, File},
+ net::SocketAddr,
+ os::unix::fs::MetadataExt,
+ path::PathBuf,
+};
pub type Serial = u64;
@@ -24,13 +30,18 @@ enum Action {
List {
peer: Option<String>,
},
- Restore {
- peer: String,
- id: String,
- destination: PathBuf,
+ Download {
+ path: PathBuf,
+ /// Which peer to download from, any if not specified
+ peer: Option<String>,
+ /// Serial of the backup to download, latest if not specified
+ serial: Option<Serial>,
},
- Backup {
+ Upload {
+ /// Path to backup file
path: PathBuf,
+ /// Which peer to upload to, all if not specified
+ peer: Option<String>,
},
}
@@ -91,12 +102,51 @@ fn main() -> Result<()> {
client.quit()?;
}
}
- Action::Restore {
- id,
- destination,
- peer,
- } => todo!(),
- Action::Backup { path } => todo!(),
+ Action::Download { path, serial, peer } => {
+ let mut peers = config.peer.iter().filter(|p| {
+ if let Some(pn) = &peer {
+ pn == &p.name
+ } else {
+ true
+ }
+ });
+ let peer = peers.next().ok_or(anyhow!("no matching peer"))?;
+ info!("connecting to {:?}", peer.name);
+ let mut client = Client::new(peer.address, &peer.shared_secret)?;
+
+ let file = File::create_new(&path)?;
+ let serial = if let Some(serial) = serial {
+ serial
+ } else {
+ client.list()?.last().ok_or(anyhow!("no backups stored"))?.2
+ };
+ println!("downloading serial={serial} from {}", peer.name);
+ client.download(serial, file)?;
+ info!("upload successful");
+ client.quit()?;
+ println!("success")
+ }
+ Action::Upload { peer, path } => {
+ let peers = config.peer.iter().filter(|p| {
+ if let Some(pn) = &peer {
+ pn == &p.name
+ } else {
+ true
+ }
+ });
+ for peer in peers {
+ info!("connecting to {:?}", peer.name);
+ println!("uploading to {}", peer.name);
+ let mut client = Client::new(peer.address, &peer.shared_secret)?;
+
+ let file = File::open(&path)?;
+ let size = file.metadata()?.size();
+ client.upload(size, file)?;
+ info!("upload successful");
+ client.quit()?;
+ }
+ println!("success")
+ }
}
Ok(())
}
diff --git a/src/server.rs b/src/server.rs
index d24f1b4..93baeb6 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,6 +1,6 @@
use crate::{Config, PeerConfig, Serial};
use anyhow::{anyhow, bail};
-use log::{error, info, warn};
+use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use std::{
fs::{create_dir_all, read_to_string, remove_file, File},
@@ -78,6 +78,7 @@ fn handle_connection(
rsock.by_ref().take(4096).read_line(&mut line)?;
let mut toks = line.trim().split(",");
let command = toks.next().ok_or(anyhow!("command missing"))?;
+ debug!("command {command:?} issued");
match command {
"quit" => break Ok(()),
"list" => {
@@ -123,7 +124,7 @@ fn handle_connection(
writeln!(wsock, "ready")?;
wsock.flush()?;
- while peerdir.read_dir()?.fold(0, |a, _| a + 1) > config.storage.versions {
+ while peerdir.read_dir()?.fold(0, |a, _| a + 1) >= config.storage.versions {
let mut dir = peerdir
.read_dir()?
.map(|e| {
@@ -143,7 +144,7 @@ fn handle_connection(
remove_file(peerdir.join(rem.to_string()))?;
}
- info!("upload from {addr} size={size}");
+ info!("upload from {addr} size={size} serial={serial}");
let mut upload = rsock.get_ref().take(size);
let mut target =
BufWriter::new(File::create_new(peerdir.join(serial.to_string()))?);
@@ -177,6 +178,7 @@ fn handle_connection(
let size = source.metadata()?.size();
let mut source = BufReader::new(source);
+ info!("download for {addr} size={size} serial={serial}");
writeln!(wsock, "ready,{size}")?;
wsock.flush()?;
copy(&mut source, wsock)?; // TODO speed limit