// // Copyright (C) <2026> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . /* 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 crate::hive_config; use crate::hive_config::HiveConfig; use crate::hive_db::{self, HiveDb}; use crate::hive_intercom::{self, BzzReq, BzzResp, IntercomError}; use crate::hive_system; #[derive(Debug)] pub enum DaemonError { IoError(std::io::Error), DBError(hive_db::DBError), ConfigError(hive_config::ConfigError), ExecCommandError(hive_system::ExecCommandError), TimestampError, } // 2. Implement From for each wrapped error type impl From for DaemonError { fn from(err: std::io::Error) -> Self { DaemonError::IoError(err) } } impl From for DaemonError { fn from(err: hive_db::DBError) -> Self { DaemonError::DBError(err) } } impl From for DaemonError { fn from(err: hive_config::ConfigError) -> Self { DaemonError::ConfigError(err) } } impl From for DaemonError { fn from(err: hive_system::ExecCommandError) -> Self { DaemonError::ExecCommandError(err) } } impl std::fmt::Display for DaemonError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { DaemonError::IoError(e) => write!(f, "IO Error: {}", e), DaemonError::DBError(e) => write!(f, "Database error: {}", e), DaemonError::ConfigError(e) => write!(f, "Config Error: {}", e), DaemonError::ExecCommandError(e) => write!(f, "Exec Command Error: {}", e), DaemonError::TimestampError => write!(f, "Timestamp Error"), } } } // Implement std::error::Error impl std::error::Error for DaemonError {} fn get_timestamp_sec() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards") .as_secs() } /// bloking function. Bloaks for configured socker recv timeout 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 => { 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); } } 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) { 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(()) } } } // doing what daemons usually beezz fn do_local_work(config: &HiveConfig, bzz_db: &mut HiveDb) -> Result<(), DaemonError> { // if updates check is turned on if config.update.check_interval != 0 { let timestamp = get_timestamp_sec(); // calculate how many hours passed sinse last update let hours_since_update_check = timestamp .checked_sub(bzz_db.update_check_timestamp) .ok_or(DaemonError::TimestampError)? / 3600; // if more than updates_check_interval specifiyed in config - check for updates if hours_since_update_check > config.update.check_interval { // 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 ); // 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); } } } // updating las tcheck for updates timestamp in db bzz_db.update_check_timestamp = get_timestamp_sec(); hive_db::write_db(&bzz_db)?; } } Ok(()) } pub fn run( mut bzz_config: HiveConfig, term_flag: Arc, reload_flag: Arc, ) -> Result<(), DaemonError> { // reading db from disk let mut bzz_db = hive_db::read_db()?; // stop flag let mut keep_going = true; // 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())?; do_local_work(&bzz_config, &mut bzz_db)?; // check termination flags if term_flag.load(Ordering::Relaxed) { println!("SIGTERM recieved - wrapping things up."); keep_going = false; } // checking reload signal flag if reload_flag.load(Ordering::Relaxed) { println!("SIGHUP recieved - reloading config and DB."); bzz_config = hive_config::read_config()?; bzz_db = hive_db::read_db()?; println!("Reloaded!"); } } println!("Exiting..."); Ok(()) }