diff --git a/Cargo.lock b/Cargo.lock index d08915a..83b7e32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. 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]] name = "anstream" version = "1.0.0" @@ -220,12 +211,12 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hive-up" -version = "0.1.1" +version = "0.1.2" dependencies = [ "clap", "nix", "postcard", - "regex", + "regex-lite", "serde", "serde_json", "signal-hook", @@ -235,9 +226,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.1" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown", @@ -326,33 +317,10 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.13.1" +name = "regex-lite" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" -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" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" [[package]] name = "rustc_version" @@ -470,9 +438,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 12f6172..8eae3f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "hive-up" -version = "0.1.1" +version = "0.1.2" edition = "2024" [dependencies] clap = { version = "4.6", features = ["derive"] } nix = { version = "0", features = ["user"] } postcard = { version = "1.1", features = ["use-std"] } -regex = "1" +regex-lite = "0.1" serde = { version = "1", features = ["derive"] } serde_json = "1.0" signal-hook = "0.4" @@ -19,7 +19,7 @@ lto = true debug = false incremental = false codegen-units = 1 -opt-level = 2 +opt-level = "s" strip = true [registry] diff --git a/README.md b/README.md index 7350fc4..2b4fb3b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,60 @@ # hive-up -Program to manage update process of Kira Linux. Creating snapshots. Dealing with pacman fail, etc. \ No newline at end of file +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 + +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 diff --git a/src/hive_db.rs b/src/hive_db.rs index 70c7f77..b6fcfdf 100644 --- a/src/hive_db.rs +++ b/src/hive_db.rs @@ -62,7 +62,8 @@ pub struct HiveDb { pub update_check_timestamp: u64, /// non zero if there is updates that can bee applied, showing numbers of packages that can be updated 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, /// datetime of last snapshot in format "%Y-%m-%dT%H:%M:%S" pub last_snapshot_time: String, @@ -101,15 +102,4 @@ pub fn write_db(db: &HiveDb) -> Result<(), DBError> { 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 { - let mut db = read_db()?; - let old_value = db.update_check_timestamp; - db.update_check_timestamp = new_timestamp; - write_db(&db)?; - Ok(old_value) -} diff --git a/src/hive_system.rs b/src/hive_system.rs index b64a31c..66335d2 100644 --- a/src/hive_system.rs +++ b/src/hive_system.rs @@ -184,12 +184,31 @@ pub fn pkg_upgrade() -> Result<(), ExecCommandError> { 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 /// returns snapshot time on sucess /// -pub fn snapshot_root() -> Result { +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 { use std::path::Path; // if snaps dir path do not exist - create it let snaps_path = Path::new(SNAPS_DIR); @@ -199,43 +218,42 @@ pub fn snapshot_root() -> Result { 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 usr_snapshot_name = format!("{}/bzz-usr-{}", SNAPS_DIR, local_time); + let var_snapshot_name = format!("{}/bzz-var-{}", SNAPS_DIR, local_time); - exec_command( - "bcachefs", - &vec!["subvolume", "snapshot", "-r", "/", &snapshot_name], - )?; + snapshot("/usr", &usr_snapshot_name)?; + snapshot("/var", &var_snapshot_name)?; Ok(local_time) } //const TMP_ROOT: &str = "/mnt/bee-root"; -pub fn list_bzzpss_snaps() -> Result, ExecCommandError> { - use regex::Regex; - use std::path::Path; +// pub fn list_bzzpss_snaps() -> Result, ExecCommandError> { +// use regex::Regex; +// use std::path::Path; - // 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(); +// // root snapshoot in a form of bzz-yyyy-mm-ddTHH-MM-SS +// 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 = Vec::new(); - // if snaps dir path do not exist return empty list - if !Path::new(SNAPS_DIR).exists() { - return Ok(res); - } +// let mut res: Vec = Vec::new(); +// // if snaps dir path do not exist return empty list +// if !Path::new(SNAPS_DIR).exists() { +// return Ok(res); +// } - for maybe_entry in fs::read_dir(SNAPS_DIR)? { - let entry = maybe_entry?; - if entry.file_type()?.is_dir() { - if let Some(dir_name) = entry.file_name().to_str() - && re.is_match(dir_name) - { - res.push(dir_name.to_string()); - } - } - } - res.sort(); - Ok(res) -} +// for maybe_entry in fs::read_dir(SNAPS_DIR)? { +// let entry = maybe_entry?; +// if entry.file_type()?.is_dir() { +// if let Some(dir_name) = entry.file_name().to_str() +// && re.is_match(dir_name) +// { +// res.push(dir_name.to_string()); +// } +// } +// } +// res.sort(); +// Ok(res) +// } pub fn check_if_run_as_root() -> bool { use nix::unistd::Uid; @@ -271,7 +289,7 @@ impl std::error::Error for SysUpgradeError {} /// pub fn sys_upgrade() -> Result { // 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 match pkg_upgrade() { Ok(_) => Ok(snap_time), @@ -281,37 +299,37 @@ pub fn sys_upgrade() -> Result { /// Gets device node aka /dev/sda1 from gpr partition label /// blkid -s PARTLABEL --match-token PARTLABEL="bzz-primary" -pub fn find_partlabel(partlabel: &str) -> Result, ExecCommandError> { - let out = Command::new("/usr/bin/blkid") - .args([ - "-s", - "PARTLABEL", - "-t", - format!("PARTLABEL=\"{}\"", partlabel).as_str(), - ]) - .output()?; - if out.status.success() { - let out_text = String::from_utf8(out.stdout)?; +// pub fn find_partlabel(partlabel: &str) -> Result, ExecCommandError> { +// let out = Command::new("/usr/bin/blkid") +// .args([ +// "-s", +// "PARTLABEL", +// "-t", +// format!("PARTLABEL=\"{}\"", partlabel).as_str(), +// ]) +// .output()?; +// if out.status.success() { +// let out_text = String::from_utf8(out.stdout)?; - let split_res = out_text.split_once(':').ok_or_else(|| { - ExecCommandError::Other(format!("Unexpected blkid output format: \"{}\"", out_text)) - })?; +// let split_res = out_text.split_once(':').ok_or_else(|| { +// ExecCommandError::Other(format!("Unexpected blkid output format: \"{}\"", out_text)) +// })?; - Ok(Some(split_res.0.to_string())) - } else { - if let Some(code) = out.status.code() - && code == 2 - { - Ok(None) - } else { - Err(ExecCommandError::Other(format!( - "Error while looking for partlabel={}: \"{}\"", - partlabel, - String::from_utf8(out.stderr).unwrap_or_else(|ex| ex.to_string()) - ))) - } - } -} +// Ok(Some(split_res.0.to_string())) +// } else { +// if let Some(code) = out.status.code() +// && code == 2 +// { +// Ok(None) +// } else { +// Err(ExecCommandError::Other(format!( +// "Error while looking for partlabel={}: \"{}\"", +// partlabel, +// 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 @@ -327,7 +345,7 @@ pub fn find_partlabel(partlabel: &str) -> Result, ExecCommandErro /// /// 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 lsblk_query = format!("PARTLABEL==\"{}\"", EFI_PARTLABEL); @@ -351,7 +369,7 @@ fn get_efi_part_info() -> Result<(String, String), ExecCommandError> { /// Boot0000* Nyan /// fn list_efi_bootnums() -> Result, ExecCommandError> { - use regex::Regex; + use regex_lite::Regex; let re = Regex::new(r#"^Boot([0-9A-F]{4})\* "#).unwrap(); let str_res = exec_command_out("efibootmgr", &vec![])?; @@ -444,7 +462,7 @@ pub fn init_efi_boot_record(maybe_kernel_cmd: &Option) -> Result<(), Exe /// Result is a sorted vector of string /// fn list_installed_kernels() -> Result, 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 mut kernel_dirs: Vec = Vec::new(); diff --git a/src/main.rs b/src/main.rs index edc8e4b..12dd0d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -163,8 +163,11 @@ struct Cli { fn cli_upgrade(bzz_config: HiveConfig) -> Result<(), HiveUpError> { 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"); + bzz_db.update_status = false; + hive_db::write_db(&bzz_db)?; match hive_system::sys_upgrade() { Ok(snap_time) => { bzz_db.update_status = true;