Changes to snapshot /usr and /var subvolumes instead of root, regex-lite adoption, and initial documentation

This commit is contained in:
2026-09-10 00:17:19 +02:00
parent 17cea4b89e
commit 20bdd217c4
6 changed files with 157 additions and 121 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.1" 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.1" 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
+2 -12
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,
@@ -101,15 +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)
}
+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();
+4 -1
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;