2 Commits
11 changed files with 201 additions and 155 deletions
Generated
+9 -41
View File
@@ -2,15 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "aho-corasick"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "1.0.0" version = "1.0.0"
@@ -220,12 +211,12 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hive-up" name = "hive-up"
version = "0.1.0" version = "0.1.2"
dependencies = [ dependencies = [
"clap", "clap",
"nix", "nix",
"postcard", "postcard",
"regex", "regex-lite",
"serde", "serde",
"serde_json", "serde_json",
"signal-hook", "signal-hook",
@@ -235,9 +226,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.14.1" version = "2.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@@ -326,33 +317,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "regex" name = "regex-lite"
version = "1.13.1" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
@@ -470,9 +438,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.4" version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
+3 -3
View File
@@ -1,13 +1,13 @@
[package] [package]
name = "hive-up" name = "hive-up"
version = "0.1.0" version = "0.1.2"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
clap = { version = "4.6", features = ["derive"] } clap = { version = "4.6", features = ["derive"] }
nix = { version = "0", features = ["user"] } nix = { version = "0", features = ["user"] }
postcard = { version = "1.1", features = ["use-std"] } postcard = { version = "1.1", features = ["use-std"] }
regex = "1" regex-lite = "0.1"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
signal-hook = "0.4" signal-hook = "0.4"
@@ -19,7 +19,7 @@ lto = true
debug = false debug = false
incremental = false incremental = false
codegen-units = 1 codegen-units = 1
opt-level = 2 opt-level = "s"
strip = true strip = true
[registry] [registry]
+57
View File
@@ -1,3 +1,60 @@
# hive-up # hive-up
Program to manage update process of Kira Linux. Creating snapshots. Dealing with pacman fail, etc. Program to manage update process of Kira Linux. Creating snapshots. Dealing with pacman fail, etc.
There should be some documantation here.
hive-up uses `/var/lib/hive-up/hyve-db.json` file as database for stuff
And `/etc/hive-up.toml` as config file.
As an daemon hive-up righ now doing only periodical checks for updates.
```text
Usage: hive-up <COMMAND>
Commands:
cli Perform local operations
remote Ask hive-up daemon something
daemon Run hive-up in daemon mode
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
```
For cli mode:
```text
Usage: hive-up cli <--upgrade|--update-boot|--init-boot-menu|--init-boot>
Options:
--upgrade Perform system upgrade
--update-boot Install new kernel into boot partition
--init-boot-menu Cleans up and recreates UFI boot menu
--init-boot Cleans up and recreates UFI boot menu and /EFI dir content
-h, --help Print help
```
For remote mode:
```text
Usage: hive-up remote <--check-updates|--updates-avail|--schedule-update>
Options:
--check-updates Ask daemon to check for updates
--updates-avail Ask daemon if there is any updates available
--schedule-update Ask daemon to perform system update on next reboot
-h, --help Print help
```
`hive-up daemon` - do not have any flags for now.
`hive-up cli --upgrade` - generally not supposed to be called by user. It get called by system service manager (dinit) after hive-up service stop (on reboot or system shutdown)
When `hive-up cli --upgrade` is called, it checks if `update_on_reboot` flag is set in **DB** or `auto_update` flag checked in **config**, and if `updates_available` in **DB** is non zero. Update routine continue only if this conditios are met.
1. It creates snapshots of `/usr` and `/var` subvolumes
2. It sets `update_status` to **false** in **DB**
3. Performs update, and if everything was fine sets `update_status` to **true** in **DB**
After this, system reboots, and on system boot Nyanit figures up what to do with failed updates if any
+12
View File
@@ -0,0 +1,12 @@
# Section (Table) headers
[update]
# Interval between updates check in hours. 0 - no auto check for updates.
check_interval = 58
# Tells hive-up to automatically download updates when awailable.
download_updates = true
# Tells hive-up to automatically perform updates during system reboot
auto_update = false
# In [kernel] section user may specify kernel cmd
# [kernel]
# cmd = ""
+2 -2
View File
@@ -176,7 +176,7 @@ fn process_remoute(
// doing what daemons usually beezz // 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 updates check is turned on
if config.update.updates_check_interval != 0 { if config.update.check_interval != 0 {
let timestamp = get_timestamp_sec(); let timestamp = get_timestamp_sec();
// calculate how many hours passed sinse last update // calculate how many hours passed sinse last update
@@ -186,7 +186,7 @@ fn do_local_work(config: &HiveConfig, bzz_db: &mut HiveDb) -> Result<(), DaemonE
/ 3600; / 3600;
// if more than updates_check_interval specifiyed in config - check for updates // if more than updates_check_interval specifiyed in config - check for updates
if hours_since_update_check > config.update.updates_check_interval { if hours_since_update_check > config.update.check_interval {
// perform update check // perform update check
println!("Cheking for updates..."); 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
+4 -4
View File
@@ -22,13 +22,13 @@ use serde::{Deserialize, Serialize};
use std::fs; use std::fs;
use toml; use toml;
const CONFIG_PATH: &str = "/etc/hive_up.toml"; const CONFIG_PATH: &str = "/etc/hive-up.toml";
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct HiveConfig { pub struct HiveConfig {
// Matches [kernel] in TOML // Matches [kernel] in TOML
pub kernel: KernelConfig, pub kernel: Option<KernelConfig>,
// Updates process config // Updates [update] in TOML
pub update: UpdateConfig, pub update: UpdateConfig,
} }
@@ -41,7 +41,7 @@ pub struct KernelConfig {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct UpdateConfig { pub struct UpdateConfig {
/// Interval between updates check in hours. 0 - no auto check for updates. /// Interval between updates check in hours. 0 - no auto check for updates.
pub updates_check_interval: u64, pub check_interval: u64,
/// Tells hive-up to automatically download updates when awailable. /// Tells hive-up to automatically download updates when awailable.
pub download_updates: bool, pub download_updates: bool,
/// Tells hive-up to automatically perform updates during system reboot. /// Tells hive-up to automatically perform updates during system reboot.
+24 -38
View File
@@ -62,7 +62,8 @@ pub struct HiveDb {
pub update_check_timestamp: u64, pub update_check_timestamp: u64,
/// non zero if there is updates that can bee applied, showing numbers of packages that can be updated /// non zero if there is updates that can bee applied, showing numbers of packages that can be updated
pub updates_available: u32, pub updates_available: u32,
/// true if last update fas successful /// true if last update was successful
/// if it is false - nyanit need to roll back snapshots
pub update_status: bool, pub update_status: bool,
/// datetime of last snapshot in format "%Y-%m-%dT%H:%M:%S" /// datetime of last snapshot in format "%Y-%m-%dT%H:%M:%S"
pub last_snapshot_time: String, pub last_snapshot_time: String,
@@ -70,10 +71,29 @@ pub struct HiveDb {
pub update_on_reboot: bool, pub update_on_reboot: bool,
} }
fn init_db() -> Result<HiveDb, DBError> {
let bzz_db = HiveDb{
update_check_timestamp: 0,
updates_available: 0,
update_status: false,
last_snapshot_time: "".into(),
update_on_reboot: false,
};
write_db(&bzz_db)?;
Ok(bzz_db)
}
pub fn read_db() -> Result<HiveDb, DBError> { pub fn read_db() -> Result<HiveDb, DBError> {
let file_content = fs::read_to_string(DB_PATH)?; if fs::exists(DB_PATH)? {
let db: HiveDb = serde_json::from_str(&file_content)?; let file_content = fs::read_to_string(DB_PATH)?;
Ok(db) let db: HiveDb = serde_json::from_str(&file_content)?;
Ok(db)
}
else {
println!("WARNING database file do not exist {}", DB_PATH);
print!("Creating default db file...");
init_db()
}
} }
pub fn write_db(db: &HiveDb) -> Result<(), DBError> { pub fn write_db(db: &HiveDb) -> Result<(), DBError> {
@@ -82,38 +102,4 @@ pub fn write_db(db: &HiveDb) -> Result<(), DBError> {
Ok(()) Ok(())
} }
///
/// writes new last update check time to db
/// and returns old db value on success
///
pub fn update_last_check_update_time(new_timestamp: u64) -> Result<u64, DBError> {
let mut db = read_db()?;
let old_value = db.update_check_timestamp;
db.update_check_timestamp = new_timestamp;
write_db(&db)?;
Ok(old_value)
}
///
/// writes new update_status to db
/// and returns old db value on success
///
pub fn update_update_status(sucess_status: bool) -> Result<bool, DBError> {
let mut db = read_db()?;
let old_value = db.update_status;
db.update_status = sucess_status;
write_db(&db)?;
Ok(old_value)
}
///
/// writes new last_update_timestamp to db
/// and returns old value on success
///
pub fn update_last_update_timestamp(new_timestamp: String) -> Result<String, DBError> {
let mut db = read_db()?;
let old_value = db.last_snapshot_time;
db.last_snapshot_time = new_timestamp;
write_db(&db)?;
Ok(old_value)
}
+81 -63
View File
@@ -184,12 +184,31 @@ pub fn pkg_upgrade() -> Result<(), ExecCommandError> {
exec_command("pacman", &vec!["-Su", "--noconfirm", "--noprogressbar"]) exec_command("pacman", &vec!["-Su", "--noconfirm", "--noprogressbar"])
} }
/// ///
/// bcachefs subvolume snapshot -r / /snap1 /// creates read only snapshoy of "subvolume" and places it into "target"]
/// bcachefs subvolume snapshot -r /subvolume /target
/// -r creates read only snapshot /// -r creates read only snapshot
/// returns snapshot time on sucess /// returns snapshot time on sucess
/// ///
pub fn snapshot_root() -> Result<String, ExecCommandError> { pub fn snapshot(subvolume: &str, target: &str) -> Result<(), ExecCommandError> {
exec_command(
"bcachefs",
&vec!["subvolume", "snapshot", "-r", subvolume, target],
)?;
Ok(())
}
///
///
/// bcachefs subvolume snapshot -r /target /SNAPS_DIR/bzz-target-yyyy-mm-ddTHH-MM-SS
/// Creates read only snapshots of /usr and /var subvolumes
/// And places them to /SNAPS_DIR/bzz-usr-yyyy-mm-ddTHH-MM-SS and /SNAPS_DIR/bzz-var-yyyy-mm-ddTHH-MM-SS
/// returns snapshot time on sucess
///
pub fn snapshot_system() -> Result<String, ExecCommandError> {
use std::path::Path; use std::path::Path;
// if snaps dir path do not exist - create it // if snaps dir path do not exist - create it
let snaps_path = Path::new(SNAPS_DIR); let snaps_path = Path::new(SNAPS_DIR);
@@ -199,43 +218,42 @@ pub fn snapshot_root() -> Result<String, ExecCommandError> {
let ts = time_format::now().unwrap(); let ts = time_format::now().unwrap();
let local_time = time_format::strftime_local("%Y-%m-%dT%H:%M:%S", ts).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 usr_snapshot_name = format!("{}/bzz-usr-{}", SNAPS_DIR, local_time);
let var_snapshot_name = format!("{}/bzz-var-{}", SNAPS_DIR, local_time);
exec_command( snapshot("/usr", &usr_snapshot_name)?;
"bcachefs", snapshot("/var", &var_snapshot_name)?;
&vec!["subvolume", "snapshot", "-r", "/", &snapshot_name],
)?;
Ok(local_time) Ok(local_time)
} }
//const TMP_ROOT: &str = "/mnt/bee-root"; //const TMP_ROOT: &str = "/mnt/bee-root";
pub fn list_bzzpss_snaps() -> Result<Vec<String>, ExecCommandError> { // pub fn list_bzzpss_snaps() -> Result<Vec<String>, ExecCommandError> {
use regex::Regex; // use regex::Regex;
use std::path::Path; // use std::path::Path;
// root snapshoot in a form of bzz-yyyy-mm-ddTHH-MM-SS // // root snapshoot in a form of bzz-yyyy-mm-ddTHH-MM-SS
let re = Regex::new(r"^bzz-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}$").unwrap(); // let re = Regex::new(r"^bzz-[a-z]+-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}$").unwrap();
let mut res: Vec<String> = Vec::new(); // let mut res: Vec<String> = Vec::new();
// if snaps dir path do not exist return empty list // // if snaps dir path do not exist return empty list
if !Path::new(SNAPS_DIR).exists() { // if !Path::new(SNAPS_DIR).exists() {
return Ok(res); // return Ok(res);
} // }
for maybe_entry in fs::read_dir(SNAPS_DIR)? { // for maybe_entry in fs::read_dir(SNAPS_DIR)? {
let entry = maybe_entry?; // let entry = maybe_entry?;
if entry.file_type()?.is_dir() { // if entry.file_type()?.is_dir() {
if let Some(dir_name) = entry.file_name().to_str() // if let Some(dir_name) = entry.file_name().to_str()
&& re.is_match(dir_name) // && re.is_match(dir_name)
{ // {
res.push(dir_name.to_string()); // res.push(dir_name.to_string());
} // }
} // }
} // }
res.sort(); // res.sort();
Ok(res) // Ok(res)
} // }
pub fn check_if_run_as_root() -> bool { pub fn check_if_run_as_root() -> bool {
use nix::unistd::Uid; use nix::unistd::Uid;
@@ -271,7 +289,7 @@ impl std::error::Error for SysUpgradeError {}
/// ///
pub fn sys_upgrade() -> Result<String, SysUpgradeError> { pub fn sys_upgrade() -> Result<String, SysUpgradeError> {
// snapshooting root // snapshooting root
let snap_time = snapshot_root().map_err(SysUpgradeError::SnapshotError)?; let snap_time = snapshot_system().map_err(SysUpgradeError::SnapshotError)?;
// try to perform packages upgrade // try to perform packages upgrade
match pkg_upgrade() { match pkg_upgrade() {
Ok(_) => Ok(snap_time), Ok(_) => Ok(snap_time),
@@ -281,37 +299,37 @@ pub fn sys_upgrade() -> Result<String, SysUpgradeError> {
/// Gets device node aka /dev/sda1 from gpr partition label /// Gets device node aka /dev/sda1 from gpr partition label
/// blkid -s PARTLABEL --match-token PARTLABEL="bzz-primary" /// blkid -s PARTLABEL --match-token PARTLABEL="bzz-primary"
pub fn find_partlabel(partlabel: &str) -> Result<Option<String>, ExecCommandError> { // pub fn find_partlabel(partlabel: &str) -> Result<Option<String>, ExecCommandError> {
let out = Command::new("/usr/bin/blkid") // let out = Command::new("/usr/bin/blkid")
.args([ // .args([
"-s", // "-s",
"PARTLABEL", // "PARTLABEL",
"-t", // "-t",
format!("PARTLABEL=\"{}\"", partlabel).as_str(), // format!("PARTLABEL=\"{}\"", partlabel).as_str(),
]) // ])
.output()?; // .output()?;
if out.status.success() { // if out.status.success() {
let out_text = String::from_utf8(out.stdout)?; // let out_text = String::from_utf8(out.stdout)?;
let split_res = out_text.split_once(':').ok_or_else(|| { // let split_res = out_text.split_once(':').ok_or_else(|| {
ExecCommandError::Other(format!("Unexpected blkid output format: \"{}\"", out_text)) // ExecCommandError::Other(format!("Unexpected blkid output format: \"{}\"", out_text))
})?; // })?;
Ok(Some(split_res.0.to_string())) // Ok(Some(split_res.0.to_string()))
} else { // } else {
if let Some(code) = out.status.code() // if let Some(code) = out.status.code()
&& code == 2 // && code == 2
{ // {
Ok(None) // Ok(None)
} else { // } else {
Err(ExecCommandError::Other(format!( // Err(ExecCommandError::Other(format!(
"Error while looking for partlabel={}: \"{}\"", // "Error while looking for partlabel={}: \"{}\"",
partlabel, // partlabel,
String::from_utf8(out.stderr).unwrap_or_else(|ex| ex.to_string()) // String::from_utf8(out.stderr).unwrap_or_else(|ex| ex.to_string())
))) // )))
} // }
} // }
} // }
/// ///
/// We need to use lsblk to find out parent device, or read topology from sysfs /// We need to use lsblk to find out parent device, or read topology from sysfs
@@ -327,7 +345,7 @@ pub fn find_partlabel(partlabel: &str) -> Result<Option<String>, ExecCommandErro
/// ///
/// ///
fn get_efi_part_info() -> Result<(String, String), ExecCommandError> { fn get_efi_part_info() -> Result<(String, String), ExecCommandError> {
use regex::Regex; use regex_lite::Regex;
let re = Regex::new(r#"^PARTN="([0-9]+)" PKNAME="([0-9a-Z]+)"$"#).unwrap(); let re = Regex::new(r#"^PARTN="([0-9]+)" PKNAME="([0-9a-Z]+)"$"#).unwrap();
let lsblk_query = format!("PARTLABEL==\"{}\"", EFI_PARTLABEL); let lsblk_query = format!("PARTLABEL==\"{}\"", EFI_PARTLABEL);
@@ -351,7 +369,7 @@ fn get_efi_part_info() -> Result<(String, String), ExecCommandError> {
/// Boot0000* Nyan /// Boot0000* Nyan
/// ///
fn list_efi_bootnums() -> Result<Vec<String>, ExecCommandError> { fn list_efi_bootnums() -> Result<Vec<String>, ExecCommandError> {
use regex::Regex; use regex_lite::Regex;
let re = Regex::new(r#"^Boot([0-9A-F]{4})\* "#).unwrap(); let re = Regex::new(r#"^Boot([0-9A-F]{4})\* "#).unwrap();
let str_res = exec_command_out("efibootmgr", &vec![])?; let str_res = exec_command_out("efibootmgr", &vec![])?;
@@ -444,7 +462,7 @@ pub fn init_efi_boot_record(maybe_kernel_cmd: &Option<String>) -> Result<(), Exe
/// Result is a sorted vector of string /// Result is a sorted vector of string
/// ///
fn list_installed_kernels() -> Result<Vec<String>, ExecCommandError> { fn list_installed_kernels() -> Result<Vec<String>, ExecCommandError> {
use regex::Regex; use regex_lite::Regex;
let re = Regex::new(r#"^[0-9]+\.[0-9]+\.[0-9]+-[0-9]-kira-lts"#).unwrap(); let re = Regex::new(r#"^[0-9]+\.[0-9]+\.[0-9]+-[0-9]-kira-lts"#).unwrap();
let mut kernel_dirs: Vec<String> = Vec::new(); let mut kernel_dirs: Vec<String> = Vec::new();
+8 -3
View File
@@ -163,8 +163,11 @@ struct Cli {
fn cli_upgrade(bzz_config: HiveConfig) -> Result<(), HiveUpError> { fn cli_upgrade(bzz_config: HiveConfig) -> Result<(), HiveUpError> {
let mut bzz_db = hive_db::read_db()?; let mut bzz_db = hive_db::read_db()?;
if bzz_db.update_on_reboot || bzz_config.update.auto_update {
if (bzz_db.updates_available !=0) && (bzz_db.update_on_reboot || bzz_config.update.auto_update) {
println!("Starting full system upgrade"); println!("Starting full system upgrade");
bzz_db.update_status = false;
hive_db::write_db(&bzz_db)?;
match hive_system::sys_upgrade() { match hive_system::sys_upgrade() {
Ok(snap_time) => { Ok(snap_time) => {
bzz_db.update_status = true; bzz_db.update_status = true;
@@ -212,11 +215,13 @@ fn main() -> Result<(), HiveUpError> {
hive_system::boot_update()?; hive_system::boot_update()?;
} else if cli_args.init_boot_menu { } else if cli_args.init_boot_menu {
println!("Init EFI boot menu..."); println!("Init EFI boot menu...");
hive_system::init_efi_boot_record(&bzz_config.kernel.cmd)?; let kernel_cmd = bzz_config.kernel.and_then(|kk| kk.cmd.clone());
hive_system::init_efi_boot_record(&kernel_cmd)?;
} else if cli_args.init_boot { } else if cli_args.init_boot {
println!("Init EFI boot menu and EFI partition content..."); println!("Init EFI boot menu and EFI partition content...");
println!("Init EFI Partition content..."); println!("Init EFI Partition content...");
hive_system::init_efi_boot_record(&bzz_config.kernel.cmd)?; let kernel_cmd = bzz_config.kernel.and_then(|kk| kk.cmd.clone());
hive_system::init_efi_boot_record(&kernel_cmd)?;
println!("Init EFI boot menu..."); println!("Init EFI boot menu...");
hive_system::init_efi_boot_partition_content()?; hive_system::init_efi_boot_partition_content()?;
} }