diff --git a/src/daemon.rs b/src/daemon.rs index a52efae..d317b53 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -18,17 +18,17 @@ hive-up daemon logic */ +use std::os::unix::net::UnixDatagram; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use std::os::unix::net::UnixDatagram; +use crate::hive_config; use crate::hive_config::HiveConfig; use crate::hive_db::{self, HiveDb}; -use crate::hive_config; +use crate::hive_intercom::{self, BzzReq, BzzResp, IntercomError}; use crate::hive_system; -use crate::hive_intercom::{self, IntercomError, BzzReq, BzzResp}; #[derive(Debug)] pub enum DaemonError { @@ -58,7 +58,6 @@ impl From for DaemonError { } } - impl From for DaemonError { fn from(err: hive_system::ExecCommandError) -> Self { DaemonError::ExecCommandError(err) @@ -88,54 +87,94 @@ fn get_timestamp_sec() -> u64 { } /// bloking function. Bloaks for configured socker recv timeout -fn process_remoute(bzz_db: &mut HiveDb, listening_socket: &UnixDatagram, recv_buf: &mut [u8]) -> Result<(), DaemonError> { +fn process_remoute( + bzz_db: &mut HiveDb, + listening_socket: &UnixDatagram, + recv_buf: &mut [u8], +) -> Result<(), DaemonError> { // waiting on socket recv_from untill timeout (also using this as a timer) - match hive_intercom::socket_recv(&listening_socket, recv_buf) { - // we get some request to process - Ok((bzz_req, client_addr)) => { - match bzz_req { - // return awailable updates number - BzzReq::UpdatesAvailable => { + match hive_intercom::socket_recv(&listening_socket, recv_buf) { + // we get some request to process + Ok((bzz_req, client_addr)) => { + match bzz_req { + // return awailable updates number + BzzReq::UpdatesAvailable => { + let resp = BzzResp::UpdatesAvailable(bzz_db.updates_available); + if let Err(ex) = + hive_intercom::socket_send(&listening_socket, &client_addr, resp) + { + println!("Error while ansvering cli request: {}", ex); + } + } + // we should apply updates if any on reboot + BzzReq::ScheduleUpdate => { + // but only if we have actually any updates + if bzz_db.updates_available > 0 { + bzz_db.update_on_reboot = true; + // keep it simple Kira + // lets just return err from the loop if something wrong with db + hive_db::write_db(&bzz_db)?; + // signal remoute that evertyhing go OK + if let Err(ex) = hive_intercom::socket_send( + &listening_socket, + &client_addr, + BzzResp::Finish, + ) { + println!("Error while ansvering cli request: {}", ex); + } + } + } + BzzReq::CheckForUpdates => { + let resp = BzzResp::Working(1); + if let Err(ex) = + hive_intercom::socket_send(&listening_socket, &client_addr, resp) + { + println!("Error while ansvering cli request: {}", ex); + } else { + // perform update check + println!("Cheking for updates..."); + // now that may fail because of bad internet and idealy we + // want to just log error and retry in other time + bzz_db.updates_available = match hive_system::check_updates() { + Ok(updates_num) => updates_num, + Err(ex) => { + println!("Error while shecking for updates: {}", ex); + 0u32 + } + }; + hive_db::write_db(&bzz_db)?; + println!( + "Found {} packages that needs to be updated.", + bzz_db.updates_available + ); + // send evailable updates numbet to client let resp = BzzResp::UpdatesAvailable(bzz_db.updates_available); - if let Err(ex) = hive_intercom::socket_send(&listening_socket,&client_addr, resp) { + if let Err(ex) = + hive_intercom::socket_send(&listening_socket, &client_addr, resp) + { println!("Error while ansvering cli request: {}", ex); } - }, - // we should apply updates if any on reboot - BzzReq::ScheduleUpdate => { - // but only if we have actually any updates - if bzz_db.updates_available > 0 { - bzz_db.update_on_reboot = true; - // keep it simple Kira - // lets just return err from the loop if something wrong with db - hive_db::write_db(&bzz_db)?; - // signal remoute that evertyhing go OK - if let Err(ex) = hive_intercom::socket_send(&listening_socket,&client_addr, BzzResp::Finish) { - println!("Error while ansvering cli request: {}", ex); - } - } } } - Ok(()) - } - // if time out - do nothing - Err(IntercomError::Timeout) => { - Ok(()) - } - // if some actual IO error then return it - Err(IntercomError::IoError(ex)) => Err(ex.into()), - // if other kinds of errors - log and continue - Err(ex) => { - println!("Got error while listenin to unix socket: {}", ex); - print!("Sleeping for 1 secong and continue..."); - thread::sleep(Duration::from_secs(1)); - Ok(()) } + Ok(()) } + // if time out - do nothing + Err(IntercomError::Timeout) => Ok(()), + // if some actual IO error then return it + Err(IntercomError::IoError(ex)) => Err(ex.into()), + // if other kinds of errors - log and continue + Err(ex) => { + println!("Got error while listenin to unix socket: {}", ex); + print!("Sleeping for 1 secong and continue..."); + thread::sleep(Duration::from_secs(1)); + Ok(()) + } + } } // doing what daemons usually beezz -fn do_local_work(config: &HiveConfig, bzz_db: &mut HiveDb)-> Result<(), DaemonError> { +fn do_local_work(config: &HiveConfig, bzz_db: &mut HiveDb) -> Result<(), DaemonError> { // if updates check is turned on if config.update.updates_check_interval != 0 { let timestamp = get_timestamp_sec(); @@ -150,7 +189,7 @@ fn do_local_work(config: &HiveConfig, bzz_db: &mut HiveDb)-> Result<(), DaemonEr if hours_since_update_check > config.update.updates_check_interval { // perform update check println!("Cheking for updates..."); - // now that may fail because of bad internet and idealy we + // now that may fail because of bad internet and idealy we // want to just log error and retry in other time bzz_db.updates_available = match hive_system::check_updates() { Ok(updates_num) => updates_num, @@ -160,14 +199,21 @@ fn do_local_work(config: &HiveConfig, bzz_db: &mut HiveDb)-> Result<(), DaemonEr } }; hive_db::write_db(&bzz_db)?; - println!("Found {} packages that needs to be updated.", bzz_db.updates_available); + println!( + "Found {} packages that needs to be updated.", + bzz_db.updates_available + ); // if auto download for updates is enabled and there is some updates - download them if config.update.download_updates && bzz_db.updates_available != 0 { println!("Downloadin updates..."); match hive_system::download_updates() { - Ok(()) => {println!("Updates downloaded...");}, - Err(ex) => {println!("Error downloading updates: {}", ex);} + Ok(()) => { + println!("Updates downloaded..."); + } + Err(ex) => { + println!("Error downloading updates: {}", ex); + } } } @@ -184,7 +230,6 @@ pub fn run( term_flag: Arc, reload_flag: Arc, ) -> Result<(), DaemonError> { - // reading db from disk let mut bzz_db = hive_db::read_db()?; @@ -194,7 +239,7 @@ pub fn run( // init udp unix socket for communication with cli and such let listening_socket = hive_intercom::socket_bind(1)?; let mut recv_buf: Vec = Vec::with_capacity(1024); - + // main loop (I love loops) (No please just do recursion properly) while keep_going { process_remoute(&mut bzz_db, &listening_socket, recv_buf.as_mut_slice())?; diff --git a/src/hive_config.rs b/src/hive_config.rs index 1d47dbd..1a0b16f 100644 --- a/src/hive_config.rs +++ b/src/hive_config.rs @@ -18,9 +18,8 @@ Functions fot system interaction */ - -use serde::{Serialize, Deserialize}; -use std::{fs, io}; +use serde::{Deserialize, Serialize}; +use std::fs; use toml; const CONFIG_PATH: &str = "/etc/hive_up.toml"; @@ -49,7 +48,6 @@ pub struct UpdateConfig { pub auto_update: bool, } - #[derive(Debug)] pub enum ConfigError { IoError(std::io::Error), @@ -82,10 +80,10 @@ impl std::fmt::Display for ConfigError { ConfigError::IoError(e) => write!(f, "IO Error: {}", e), ConfigError::TomlParseError(e) => { write!(f, "TOML parse error: {}", e) - }, + } ConfigError::TomlSerError(e) => { write!(f, "TOML serialize error: {}", e) - }, + } } } } @@ -95,11 +93,10 @@ impl std::error::Error for ConfigError {} pub fn read_config() -> Result { let file_content = fs::read_to_string(CONFIG_PATH)?; - let config:HiveConfig = toml::from_str(&file_content)?; + let config: HiveConfig = toml::from_str(&file_content)?; Ok(config) } - pub fn change_updates_check_interval(new_interval: u64) -> Result { let mut config: HiveConfig = read_config()?; config.update.updates_check_interval = new_interval; @@ -107,5 +104,4 @@ pub fn change_updates_check_interval(new_interval: u64) -> Result Result Result<(), IntercomError> { +pub fn socket_send( + socket: &UnixDatagram, + client_addr: &SocketAddr, + resp: BzzResp, +) -> Result<(), IntercomError> { let resp_buff = postcard::to_stdvec(&resp)?; socket.send_to_addr(resp_buff.as_slice(), client_addr)?; Ok(()) @@ -141,28 +148,36 @@ pub fn socket_recv( fn send_and_recv(request: BzzReq) -> Result { let socket = UnixDatagram::unbound()?; socket.connect(SOCKET_PATH)?; - socket.set_read_timeout(Some(Duration::from_secs(CLINET_READ_TIMEOUT)))?; + socket.set_read_timeout(Some(Duration::from_secs(CLINET_READ_TIMEOUT_SECONDS)))?; let encoded_buf = postcard::to_stdvec(&request)?; socket.send(encoded_buf.as_slice())?; let mut recv_buf: Vec = Vec::with_capacity(1024); - match socket.recv(recv_buf.as_mut_slice()) { - Ok(rec_count) => { - if rec_count != 0 { - println!("DEBUG: Total bytes recieved {}", rec_count); - let decoded_resp: BzzResp = postcard::from_bytes(&recv_buf)?; - Ok(decoded_resp) - } else { - Err(IntercomError::ConnectionClosed) + + loop { + match socket.recv(recv_buf.as_mut_slice()) { + Ok(rec_count) => { + if rec_count != 0 { + println!("DEBUG: Total bytes recieved {}", rec_count); + let decoded_resp: BzzResp = postcard::from_bytes(&recv_buf)?; + if let BzzResp::Working(progress) = decoded_resp { + println!("Working... {}", progress); + continue; + } else { + return Ok(decoded_resp); + } + } else { + return Err(IntercomError::ConnectionClosed); + } } + Err(e) => return Err(e.into()), } - Err(e) => Err(e.into()), } } -fn updates_available() -> Result { +pub fn client_updates_available() -> Result { let resp = send_and_recv(BzzReq::UpdatesAvailable)?; match resp { BzzResp::UpdatesAvailable(updates_count) => Ok(updates_count), @@ -170,3 +185,12 @@ fn updates_available() -> Result { _ => Err(IntercomError::UnexpectedResponse), } } + +pub fn client_check_for_updates() -> Result { + let resp = send_and_recv(BzzReq::CheckForUpdates)?; + match resp { + BzzResp::UpdatesAvailable(updates_count) => Ok(updates_count), + BzzResp::Error(ex) => Err(IntercomError::ServerError(ex)), + _ => Err(IntercomError::UnexpectedResponse), + } +} diff --git a/src/hive_system.rs b/src/hive_system.rs index e82673e..723e152 100644 --- a/src/hive_system.rs +++ b/src/hive_system.rs @@ -22,7 +22,6 @@ use std::fs; use std::process::Command; use time_format; - const SNAPS_DIR: &str = "/bzz_snaps"; #[derive(Debug)] @@ -140,11 +139,21 @@ pub fn check_updates() -> Result { // Check for updates let out = Command::new("pacman").arg("-Qu").output()?; if out.status.success() { - Ok(String::from_utf8(out.stdout)?.lines().count().try_into().unwrap()) + Ok(String::from_utf8(out.stdout)? + .lines() + .count() + .try_into() + .unwrap()) } else { - if let Some(code) = out.status.code() && code == 1 { Ok(0) } - else { - Err(ExecCommandError::Other(format!("pacman -Qu failed: {}", String::from_utf8(out.stderr)?))) + if let Some(code) = out.status.code() + && code == 1 + { + Ok(0) + } else { + Err(ExecCommandError::Other(format!( + "pacman -Qu failed: {}", + String::from_utf8(out.stderr)? + ))) } } } @@ -152,7 +161,7 @@ pub fn check_updates() -> Result { /// /// sudo pacman -Suw --noconfirm --noprogressbar /// Download updates (need to check for updates first) -/// +/// pub fn download_updates() -> Result<(), ExecCommandError> { exec_command("pacman", &vec!["-Suw", "--noconfirm", "--noprogressbar"]) } @@ -160,24 +169,34 @@ pub fn download_updates() -> Result<(), ExecCommandError> { /// /// sudo pacman -Su --noconfirm --noprogressbar /// Perform updates -/// -pub fn do_updates() -> Result<(), ExecCommandError> { +/// +pub fn pkg_upgrade() -> Result<(), ExecCommandError> { exec_command("pacman", &vec!["-Su", "--noconfirm", "--noprogressbar"]) } - /// /// bcachefs subvolume snapshot -r / /snap1 /// -r creates read only snapshot -/// returns snapshot name on sucess +/// returns snapshot time on sucess +/// pub fn snapshot_root() -> Result { + use std::path::Path; + // if snaps dir path do not exist - create it + let snaps_path = Path::new(SNAPS_DIR); + if !snaps_path.exists() { + fs::create_dir(snaps_path)?; + } + let ts = time_format::now().unwrap(); let local_time = time_format::strftime_local("%Y-%m-%dT%H:%M:%S", ts).unwrap(); - let snapshot_name = format!("{}/bzz-{}",SNAPS_DIR,local_time); + let snapshot_name = format!("{}/bzz-{}", SNAPS_DIR, local_time); - exec_command("bcachefs", &vec!["subvolume", "snapshot", "-r", "/", &snapshot_name])?; + exec_command( + "bcachefs", + &vec!["subvolume", "snapshot", "-r", "/", &snapshot_name], + )?; - Ok(snapshot_name) + Ok(local_time) } //const TMP_ROOT: &str = "/mnt/bee-root"; @@ -208,7 +227,6 @@ pub fn list_bzzpss_snaps() -> Result, ExecCommandError> { Ok(res) } - pub fn check_if_run_as_root() -> bool { use nix::unistd::Uid; if Uid::effective().is_root() { @@ -216,4 +234,37 @@ pub fn check_if_run_as_root() -> bool { } else { false } -} \ No newline at end of file +} + +#[derive(Debug)] +pub enum SysUpgradeError { + SnapshotError(ExecCommandError), + PkgUpgradeErr((String, ExecCommandError)), +} + +impl std::fmt::Display for SysUpgradeError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + SysUpgradeError::SnapshotError(e) => write!(f, "Snapshot creation error: {}", e), + SysUpgradeError::PkgUpgradeErr((_, e)) => write!(f, "Packages upgrade error: {}", e), + } + } +} + +// Implement std::error::Error +impl std::error::Error for SysUpgradeError {} + +/// +/// Perform system upgrade +/// Reruns root snapshot name on success +/// In case of upgrade error SysUpgradeError::PkgUpgradeErr contains snapshot time is str format +/// +pub fn sys_upgrade() -> Result { + // snapshooting root + let snap_time = snapshot_root().map_err(SysUpgradeError::SnapshotError)?; + // try to perform packages upgrade + match pkg_upgrade() { + Ok(_) => Ok(snap_time), + Err(ex) => Err(SysUpgradeError::PkgUpgradeErr((snap_time, ex))), + } +} diff --git a/src/main.rs b/src/main.rs index 61b76da..2135b42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,33 +18,109 @@ use clap::{Args, Parser}; use signal_hook::{consts::SIGINT, iterator::Signals}; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::{error::Error, thread, time::Duration}; +use std; mod hive_system; -use hive_system::ExecCommandError; mod hive_config; -use hive_config::{HiveConfig, KernelConfig}; + +use crate::hive_system::SysUpgradeError; mod daemon; mod hive_db; mod hive_intercom; +#[derive(Debug)] +pub enum HiveUpError { + IoError(std::io::Error), + HiveSystemCommandError(hive_system::ExecCommandError), + HiveDbError(hive_db::DBError), + HiveConfigError(hive_config::ConfigError), + HiveIntercomError(hive_intercom::IntercomError), + HiveDaemonErr(daemon::DaemonError), + Other(String), +} + +impl From for HiveUpError { + fn from(err: std::io::Error) -> Self { + HiveUpError::IoError(err) + } +} + +impl From for HiveUpError { + fn from(err: hive_system::ExecCommandError) -> Self { + HiveUpError::HiveSystemCommandError(err) + } +} + +impl From for HiveUpError { + fn from(err: hive_db::DBError) -> Self { + HiveUpError::HiveDbError(err) + } +} + +impl From for HiveUpError { + fn from(err: hive_config::ConfigError) -> Self { + HiveUpError::HiveConfigError(err) + } +} + +impl From for HiveUpError { + fn from(err: hive_intercom::IntercomError) -> Self { + HiveUpError::HiveIntercomError(err) + } +} + +impl From for HiveUpError { + fn from(err: daemon::DaemonError) -> Self { + HiveUpError::HiveDaemonErr(err) + } +} + +impl From for HiveUpError { + fn from(err: String) -> Self { + HiveUpError::Other(err) + } +} + +impl From<&str> for HiveUpError { + fn from(err: &str) -> Self { + HiveUpError::Other(err.to_string()) + } +} + +impl std::fmt::Display for HiveUpError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + HiveUpError::IoError(e) => write!(f, "IO Error: {}", e), + HiveUpError::HiveSystemCommandError(e) => write!(f, "ExecCommandError: {}", e), + HiveUpError::HiveDbError(ex) => write!(f, "DBError: {}", ex), + HiveUpError::HiveConfigError(ex) => write!(f, "Config error: {}", ex), + HiveUpError::HiveIntercomError(e) => write!(f, "IntercomError: {}", e), + HiveUpError::HiveDaemonErr(e) => write!(f, "HiveDaemonErr: {}", e), + HiveUpError::Other(msg) => write!(f, "Error: {}", msg), + } + } +} + +// Implement std::error::Error +impl std::error::Error for HiveUpError {} + // Define the mutually exclusive group #[derive(Args, Debug)] #[group(required = true, multiple = false)] // 'multiple = false' enforces exclusivity struct HiveUpMode { /// Check for updates, prints number of updates available #[arg(long)] - check: bool, + check_updates: bool, - /// Perform system update + /// Check for available updates, prints number of updates available #[arg(long)] - update: bool, + updates_available: bool, - /// Boot update + /// Perform system upgrade #[arg(long)] - boot_update: bool, + upgrade: bool, /// Run hive-up in daemon mode #[arg(long)] @@ -60,7 +136,7 @@ struct Cli { mode: HiveUpMode, } -fn main() -> Result<(), String> { +fn main() -> Result<(), HiveUpError> { // registering signals handling let term_flag: Arc = Arc::new(AtomicBool::new(false)); let reload_flag = Arc::new(AtomicBool::new(false)); @@ -71,39 +147,42 @@ fn main() -> Result<(), String> { let args = Cli::parse(); if !hive_system::check_if_run_as_root() { - return Err("Error: This program requires root privileges.".to_string()); + return Err("Error: This program requires root privileges.".into()); } // read config - let bzz_config = match hive_config::read_config() { - Ok(conf) => conf, - Err(ex) => { - return Err(format!("Error reading hive-up config file: {}", ex)); - } - }; + let bzz_config = hive_config::read_config()?; - // Logic to check which one was set - if args.mode.check { - match hive_system::check_updates() { - Ok(up_num) => println!("Updates awailable: {}", up_num), - Err(ex) => println!("Error: {}", ex), + // Check CMD flags + if args.mode.check_updates { + let up_num = hive_intercom::client_check_for_updates()?; + println!("Updates awailable: {}", up_num); + } else if args.mode.updates_available { + let up_num = hive_intercom::client_updates_available()?; + println!("Updates awailable: {}", up_num); + } else if args.mode.upgrade { + let mut bzz_db = hive_db::read_db()?; + if bzz_db.update_on_reboot { + println!("Starting full system upgrade"); + match hive_system::sys_upgrade() { + Ok(snap_time) => { + bzz_db.update_status = true; + bzz_db.update_on_reboot = false; + bzz_db.last_snapshot_time = snap_time; + hive_db::write_db(&bzz_db)?; + } + Err(SysUpgradeError::PkgUpgradeErr((snap_time, ex))) => { + bzz_db.update_status = false; + bzz_db.update_on_reboot = false; + bzz_db.last_snapshot_time = snap_time; + hive_db::write_db(&bzz_db)?; + return Err(ex.into()); + } + Err(ex) => return Err(ex.to_string().into()), + } } - } else if args.mode.boot_update { - // update boot - println!("update mode"); - } else if args.mode.update { - println!("update mode"); } else if args.mode.daemon { - if let Err(ex) = daemon::run(bzz_config, term_flag.clone(), reload_flag.clone()) { - return Err(format!("Hive-Up daemon error: {}", ex)); - } - - // let snaps = hive_system::list_bzzpss_snaps().unwrap(); - // println!("Snaps num: {}", snaps.len()); - // for sn in snaps { - // println!("Snap: {}", sn); - // } + daemon::run(bzz_config, term_flag.clone(), reload_flag.clone())?; } - Ok(()) }