5 Commits
12 changed files with 258 additions and 1066 deletions
Generated
+13 -221
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"
@@ -61,26 +52,11 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
dependencies = [
"critical-section",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.13.1" version = "2.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@@ -134,39 +110,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.5" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.2" version = "1.0.2"
@@ -183,35 +132,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.17.1" version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heapless"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
dependencies = [
"atomic-polyfill",
"hash32",
"rustc_version",
"serde",
"spin",
"stable_deref_trait",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
@@ -220,14 +146,12 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hive-up" name = "hive-up"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"clap", "clap",
"nix", "nix",
"postcard", "regex-lite",
"regex",
"serde", "serde",
"serde_json",
"signal-hook", "signal-hook",
"time-format", "time-format",
"toml", "toml",
@@ -235,9 +159,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",
@@ -249,33 +173,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.189" version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.31.3" version = "0.31.3"
@@ -294,19 +197,6 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "postcard"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
dependencies = [
"cobs",
"embedded-io 0.4.0",
"embedded-io 0.6.1",
"heapless",
"serde",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.107" version = "1.0.107"
@@ -326,54 +216,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]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]] [[package]]
name = "serde" name = "serde"
@@ -405,19 +251,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_json"
version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "1.1.1" version = "1.1.1"
@@ -447,21 +280,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "spin"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@@ -470,35 +288,15 @@ 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",
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "thiserror"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "time-format" name = "time-format"
version = "1.2.3" version = "1.2.3"
@@ -507,9 +305,9 @@ checksum = "fb54c799611991d3d4a66fee3ff17b9f600c2a0f8c0d34e8209dbd153ff0a87b"
[[package]] [[package]]
name = "toml" name = "toml"
version = "1.1.5+spec-1.1.0" version = "1.1.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12c0ba9680044b4ce98d391a62094047eada0d64860b80166c39f4a6b5640785" checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde_core", "serde_core",
@@ -576,9 +374,3 @@ name = "winnow"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+3 -5
View File
@@ -1,15 +1,13 @@
[package] [package]
name = "hive-up" name = "hive-up"
version = "0.1.0" version = "0.2.0"
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"] } regex-lite = "0.1"
regex = "1"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
signal-hook = "0.4" signal-hook = "0.4"
time-format = "1.2" time-format = "1.2"
toml = "1.1" toml = "1.1"
@@ -19,7 +17,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
@@ -9,4 +9,5 @@ Target = linux-kira-server-lts
[Action] [Action]
Description = Running custom command after kernel update Description = Running custom command after kernel update
When = PostTransaction When = PostTransaction
Exec = /usr/bin/hive-up cli --update-boot Exec = /usr/bin/hive-up boot update
+5
View File
@@ -0,0 +1,5 @@
# Section (Table) headers
# In [kernel] section user may specify kernel cmd
# [kernel]
# cmd = ""
-6
View File
@@ -1,6 +0,0 @@
type = process
command = /usr/bin/hive-up daemon
stop-command = /usr/bin/hive-up cli --upgrade
depends-on = network.target
logfile = /var/log/dinit/hive-up.log
log-type = file
-264
View File
@@ -1,264 +0,0 @@
// <hive-up - update program fot Kira Linux.>
// Copyright (C) <2026> <Kira Foundation>
// 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 <https://www.gnu.org/licenses/>.
/*
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<std::io::Error> for DaemonError {
fn from(err: std::io::Error) -> Self {
DaemonError::IoError(err)
}
}
impl From<hive_db::DBError> for DaemonError {
fn from(err: hive_db::DBError) -> Self {
DaemonError::DBError(err)
}
}
impl From<hive_config::ConfigError> for DaemonError {
fn from(err: hive_config::ConfigError) -> Self {
DaemonError::ConfigError(err)
}
}
impl From<hive_system::ExecCommandError> 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);
}
}
// 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)
{
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.updates_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.updates_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<AtomicBool>,
reload_flag: Arc<AtomicBool>,
) -> 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<u8> = 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(())
}
+2 -13
View File
@@ -22,14 +22,12 @@ 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
pub update: UpdateConfig,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@@ -38,15 +36,6 @@ pub struct KernelConfig {
pub cmd: Option<String>, pub cmd: Option<String>,
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateConfig {
/// Interval between updates check in hours. 0 - no auto check for updates.
pub updates_check_interval: u64,
/// Tells hive-up to automatically download updates when awailable.
pub download_updates: bool,
/// Tells hive-up to automatically perform updates during system reboot.
pub auto_update: bool,
}
#[derive(Debug)] #[derive(Debug)]
pub enum ConfigError { pub enum ConfigError {
-119
View File
@@ -1,119 +0,0 @@
// <hive-up - update program fot Kira Linux.>
// Copyright (C) <2026> <Kira Foundation>
// 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 <https://www.gnu.org/licenses/>.
/*
Functions for reading and writyng daemon and updates state
*/
const DB_PATH: &str = "/var/lib/hive-up/hyve-db.json";
use serde::{Deserialize, Serialize};
use std::fs;
#[derive(Debug)]
pub enum DBError {
IoError(std::io::Error),
JsonError(serde_json::Error),
}
// 2. Implement From for each wrapped error type
impl From<std::io::Error> for DBError {
fn from(err: std::io::Error) -> Self {
DBError::IoError(err)
}
}
impl From<serde_json::Error> for DBError {
fn from(err: serde_json::Error) -> Self {
DBError::JsonError(err)
}
}
impl std::fmt::Display for DBError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
DBError::IoError(e) => write!(f, "IO Error: {}", e),
DBError::JsonError(e) => {
write!(f, "Json parse error: {}", e)
}
}
}
}
// Implement std::error::Error
impl std::error::Error for DBError {}
#[derive(Serialize, Deserialize, Debug)]
pub struct HiveDb {
/// Timestamp of last check for updates in seconds from UNIX_EPOCH
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
pub update_status: bool,
/// datetime of last snapshot in format "%Y-%m-%dT%H:%M:%S"
pub last_snapshot_time: String,
/// set to true if we want to apply available updates on reboot
pub update_on_reboot: bool,
}
pub fn read_db() -> Result<HiveDb, DBError> {
let file_content = fs::read_to_string(DB_PATH)?;
let db: HiveDb = serde_json::from_str(&file_content)?;
Ok(db)
}
pub fn write_db(db: &HiveDb) -> Result<(), DBError> {
let serialized_db = serde_json::to_string(db)?;
fs::write(DB_PATH, serialized_db)?;
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)
}
-205
View File
@@ -1,205 +0,0 @@
// <hive-up - update program fot Kira Linux.>
// Copyright (C) <2026> <Kira Foundation>
// 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 <https://www.gnu.org/licenses/>.
/*
hive-up unix socket communication
*/
use postcard;
use serde::{Deserialize, Serialize};
use std::fs;
use std::os::unix::net::{SocketAddr, UnixDatagram};
use std::time::Duration;
pub const SOCKET_DIR: &str = "/run/hive-up";
pub const SOCKET_PATH: &str = "/run/hive-up/hive-intercom.soc";
pub const CLINET_READ_TIMEOUT_SECONDS: u64 = 88;
#[derive(Debug)]
pub enum IntercomError {
IoError(std::io::Error),
PostcardError(postcard::Error),
ConnectionClosed,
Timeout,
UnexpectedResponse,
ServerError(String),
}
// Wrapping IO errors but separating time out error
impl From<std::io::Error> for IntercomError {
fn from(err: std::io::Error) -> Self {
if err.kind() == std::io::ErrorKind::TimedOut
|| err.kind() == std::io::ErrorKind::WouldBlock
{
IntercomError::Timeout
} else {
IntercomError::IoError(err)
}
}
}
impl From<postcard::Error> for IntercomError {
fn from(err: postcard::Error) -> Self {
IntercomError::PostcardError(err)
}
}
impl From<String> for IntercomError {
fn from(err: String) -> Self {
IntercomError::ServerError(err)
}
}
impl std::fmt::Display for IntercomError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
IntercomError::IoError(e) => write!(f, "IO Error: {}", e),
IntercomError::PostcardError(e) => {
write!(f, "Data parsing (postcard) error: {}", e)
}
IntercomError::Timeout => write!(f, "Operation Timeout"),
IntercomError::ConnectionClosed => write!(f, "Connection closed"),
IntercomError::UnexpectedResponse => write!(f, "Unexpected response"),
IntercomError::ServerError(err) => write!(f, "Server side error: {}", err),
}
}
}
// Implement std::error::Error
impl std::error::Error for IntercomError {}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum BzzResp {
/// signalls progress of a task as u16 integer.
/// interpretation is up to you.
Working(u16),
Finish,
UpdatesAvailable(u32),
Error(String),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[repr(u8)]
pub enum BzzReq {
UpdatesAvailable,
CheckForUpdates,
ScheduleUpdate,
}
pub fn socket_bind(read_timeout_sec: u64) -> Result<UnixDatagram, std::io::Error> {
// create socket directory if it is does not exist
if let Ok(path_exist) = fs::exists(SOCKET_DIR)
&& path_exist == false
{
fs::create_dir(SOCKET_DIR)?;
}
// remove socket file if any present
let _ = fs::remove_file(SOCKET_PATH);
// bind datagram unix socket
let socket = UnixDatagram::bind(SOCKET_PATH)?;
socket.set_read_timeout(Some(Duration::from_secs(read_timeout_sec)))?;
Ok(socket)
}
// for daemon side
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(())
}
// for daemon side
pub fn socket_recv(
socket: &UnixDatagram,
recv_buf: &mut [u8],
) -> Result<(BzzReq, SocketAddr), IntercomError> {
match socket.recv_from(recv_buf) {
Ok((rec_count, client_addr)) => {
if rec_count != 0 {
println!("DEBUG: Total bytes recieved {}", rec_count);
let decoded_req: BzzReq = postcard::from_bytes(&recv_buf)?;
Ok((decoded_req, client_addr))
} else {
Err(IntercomError::ConnectionClosed)
}
}
Err(ex) => Err(ex.into()),
}
}
fn send_and_recv(request: BzzReq) -> Result<BzzResp, IntercomError> {
let socket = UnixDatagram::unbound()?;
socket.connect(SOCKET_PATH)?;
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<u8> = Vec::with_capacity(1024);
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()),
}
}
}
pub fn client_updates_available() -> Result<u32, IntercomError> {
let resp = send_and_recv(BzzReq::UpdatesAvailable)?;
match resp {
BzzResp::UpdatesAvailable(updates_count) => Ok(updates_count),
BzzResp::Error(ex) => Err(IntercomError::ServerError(ex)),
_ => Err(IntercomError::UnexpectedResponse),
}
}
pub fn client_check_for_updates() -> Result<u32, IntercomError> {
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),
}
}
pub fn client_schedule_update() -> Result<(), IntercomError> {
let resp = send_and_recv(BzzReq::ScheduleUpdate)?;
match resp {
BzzResp::Finish => Ok(()),
BzzResp::Error(ex) => Err(IntercomError::ServerError(ex)),
_ => Err(IntercomError::UnexpectedResponse),
}
}
+134 -116
View File
@@ -143,53 +143,72 @@ pub fn exec_command_out(cmd: &str, args: &Vec<&str>) -> Result<String, ExecComma
/// pacman -Sy /// pacman -Sy
/// pacman -Qu /// pacman -Qu
/// returns number of updatable packages, returns 0 if there is no updates awailable /// returns number of updatable packages, returns 0 if there is no updates awailable
pub fn check_updates() -> Result<u32, ExecCommandError> { // pub fn check_updates() -> Result<u32, ExecCommandError> {
// Synchronize pacman DB // // Synchronize pacman DB
exec_command("pacman", &vec!["-Sy"])?; // exec_command("pacman", &vec!["-Sy"])?;
// Check for updates // // Check for updates
let out = Command::new("pacman").arg("-Qu").output()?; // let out = Command::new("pacman").arg("-Qu").output()?;
if out.status.success() { // if out.status.success() {
Ok(String::from_utf8(out.stdout)? // Ok(String::from_utf8(out.stdout)?
.lines() // .lines()
.count() // .count()
.try_into() // .try_into()
.unwrap()) // .unwrap())
} else { // } else {
if let Some(code) = out.status.code() // if let Some(code) = out.status.code()
&& code == 1 // && code == 1
{ // {
Ok(0) // Ok(0)
} else { // } else {
Err(ExecCommandError::Other(format!( // Err(ExecCommandError::Other(format!(
"pacman -Qu failed: {}", // "pacman -Qu failed: {}",
String::from_utf8(out.stderr)? // String::from_utf8(out.stderr)?
))) // )))
} // }
} // }
} // }
/// ///
/// sudo pacman -Suw --noconfirm --noprogressbar /// sudo pacman -Suw --noconfirm --noprogressbar
/// Download updates (need to check for updates first) /// Download updates (need to check for updates first)
/// ///
pub fn download_updates() -> Result<(), ExecCommandError> { // pub fn download_updates() -> Result<(), ExecCommandError> {
exec_command("pacman", &vec!["-Suw", "--noconfirm", "--noprogressbar"]) // exec_command("pacman", &vec!["-Suw", "--noconfirm", "--noprogressbar"])
} // }
/// ///
/// sudo pacman -Su --noconfirm --noprogressbar /// sudo pacman -Su --noconfirm --noprogressbar
/// Perform updates /// Perform updates
/// ///
pub fn pkg_upgrade() -> Result<(), ExecCommandError> { // 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;
@@ -246,72 +264,72 @@ pub fn check_if_run_as_root() -> bool {
} }
} }
#[derive(Debug)] // #[derive(Debug)]
pub enum SysUpgradeError { // pub enum SysUpgradeError {
SnapshotError(ExecCommandError), // SnapshotError(ExecCommandError),
PkgUpgradeErr((String, ExecCommandError)), // PkgUpgradeErr((String, ExecCommandError)),
} // }
impl std::fmt::Display for SysUpgradeError { // impl std::fmt::Display for SysUpgradeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { // fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { // match self {
SysUpgradeError::SnapshotError(e) => write!(f, "Snapshot creation error: {}", e), // SysUpgradeError::SnapshotError(e) => write!(f, "Snapshot creation error: {}", e),
SysUpgradeError::PkgUpgradeErr((_, e)) => write!(f, "Packages upgrade error: {}", e), // SysUpgradeError::PkgUpgradeErr((_, e)) => write!(f, "Packages upgrade error: {}", e),
} // }
} // }
} // }
// Implement std::error::Error // // Implement std::error::Error
impl std::error::Error for SysUpgradeError {} // impl std::error::Error for SysUpgradeError {}
/// ///
/// Perform system upgrade /// Perform system upgrade
/// Reruns root snapshot name on success /// Reruns root snapshot name on success
/// In case of upgrade error SysUpgradeError::PkgUpgradeErr contains snapshot time is str format /// In case of upgrade error SysUpgradeError::PkgUpgradeErr contains snapshot time is str format
/// ///
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),
Err(ex) => Err(SysUpgradeError::PkgUpgradeErr((snap_time, ex))), // Err(ex) => Err(SysUpgradeError::PkgUpgradeErr((snap_time, ex))),
} // }
} // }
/// 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();
+41 -115
View File
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use clap::{Args, Parser, Subcommand}; use clap::{Parser, Subcommand};
use signal_hook; use signal_hook;
use std; use std;
use std::sync::Arc; use std::sync::Arc;
@@ -24,21 +24,11 @@ mod hive_system;
mod hive_config; mod hive_config;
use crate::hive_config::HiveConfig;
use crate::hive_system::SysUpgradeError;
mod daemon;
mod hive_db;
mod hive_intercom;
#[derive(Debug)] #[derive(Debug)]
pub enum HiveUpError { pub enum HiveUpError {
IoError(std::io::Error), IoError(std::io::Error),
HiveSystemCommandError(hive_system::ExecCommandError), HiveSystemCommandError(hive_system::ExecCommandError),
HiveDbError(hive_db::DBError),
HiveConfigError(hive_config::ConfigError), HiveConfigError(hive_config::ConfigError),
HiveIntercomError(hive_intercom::IntercomError),
HiveDaemonErr(daemon::DaemonError),
Other(String), Other(String),
} }
@@ -54,30 +44,12 @@ impl From<hive_system::ExecCommandError> for HiveUpError {
} }
} }
impl From<hive_db::DBError> for HiveUpError {
fn from(err: hive_db::DBError) -> Self {
HiveUpError::HiveDbError(err)
}
}
impl From<hive_config::ConfigError> for HiveUpError { impl From<hive_config::ConfigError> for HiveUpError {
fn from(err: hive_config::ConfigError) -> Self { fn from(err: hive_config::ConfigError) -> Self {
HiveUpError::HiveConfigError(err) HiveUpError::HiveConfigError(err)
} }
} }
impl From<hive_intercom::IntercomError> for HiveUpError {
fn from(err: hive_intercom::IntercomError) -> Self {
HiveUpError::HiveIntercomError(err)
}
}
impl From<daemon::DaemonError> for HiveUpError {
fn from(err: daemon::DaemonError) -> Self {
HiveUpError::HiveDaemonErr(err)
}
}
impl From<String> for HiveUpError { impl From<String> for HiveUpError {
fn from(err: String) -> Self { fn from(err: String) -> Self {
HiveUpError::Other(err) HiveUpError::Other(err)
@@ -95,10 +67,7 @@ impl std::fmt::Display for HiveUpError {
match self { match self {
HiveUpError::IoError(e) => write!(f, "IO Error: {}", e), HiveUpError::IoError(e) => write!(f, "IO Error: {}", e),
HiveUpError::HiveSystemCommandError(e) => write!(f, "ExecCommandError: {}", e), HiveUpError::HiveSystemCommandError(e) => write!(f, "ExecCommandError: {}", e),
HiveUpError::HiveDbError(ex) => write!(f, "DBError: {}", ex),
HiveUpError::HiveConfigError(ex) => write!(f, "Config error: {}", 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), HiveUpError::Other(msg) => write!(f, "Error: {}", msg),
} }
} }
@@ -107,49 +76,34 @@ impl std::fmt::Display for HiveUpError {
// Implement std::error::Error // Implement std::error::Error
impl std::error::Error for HiveUpError {} impl std::error::Error for HiveUpError {}
// Define the mutually exclusive group #[derive(Subcommand, Debug)]
#[derive(Args, Debug)] #[group(required = true)]
#[group(required = true, multiple = false)] enum BootCommand {
struct RemoteArgs { /// Install new kernel into boot partition
/// Ask daemon to check for updates Update,
#[arg(long)] /// Cleans up and recreates UFI boot menu
check_updates: bool, EfiMenuInit,
/// Cleans up and recreates UFI boot menu and /EFI dir content
/// Ask daemon if there is any updates available Init,
#[arg(long)]
updates_avail: bool,
/// Ask daemon to perform system update on next reboot
#[arg(long)]
schedule_update: bool,
} }
#[derive(Args, Debug)] #[derive(Subcommand, Debug)]
#[group(required = true, multiple = false)] #[group(required = true)]
struct CliArgs { enum Pac {
/// Perform system upgrade /// Creates snapshoots of /usr and /var subvolumea
#[arg(long)] SnapshotSystem,
upgrade: bool,
/// Install new kernel into boot partition
#[arg(long)]
update_boot: bool,
/// Cleans up and recreates UFI boot menu
#[arg(long)]
init_boot_menu: bool,
/// Cleans up and recreates UFI boot menu and /EFI dir content
#[arg(long)]
init_boot: bool,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
#[group(required = true)] // 'multiple = false' enforces exclusivity #[group(required = true)] // 'multiple = false' enforces exclusivity
enum HiveUpMode { enum HiveUpMode {
/// Perform local operations /// Manage system boot
Cli(CliArgs), #[command(subcommand)]
/// Ask hive-up daemon something Boot(BootCommand),
Remote(RemoteArgs),
/// Run hive-up in daemon mode /// Intended to be used by pacman hooks
Daemon, #[command(subcommand)]
Pac(Pac),
} }
/// Update program fot Kira Linux :3 /// Update program fot Kira Linux :3
@@ -161,37 +115,14 @@ struct Cli {
mode: HiveUpMode, mode: HiveUpMode,
} }
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 {
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()),
}
}
Ok(())
}
fn main() -> Result<(), HiveUpError> { fn main() -> Result<(), HiveUpError> {
// registering signals handling // registering signals handling
// we really do not want for this program to get terminated in a middle of something,
// so doing nothing with signals.
// second sigtrm should kill it tho
let term_flag: Arc<AtomicBool> = Arc::new(AtomicBool::new(false)); let term_flag: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
let reload_flag = Arc::new(AtomicBool::new(false));
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term_flag)).unwrap(); signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term_flag)).unwrap();
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&term_flag)).unwrap(); signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&term_flag)).unwrap();
signal_hook::flag::register(signal_hook::consts::SIGHUP, Arc::clone(&reload_flag)).unwrap();
let args = Cli::parse(); let args = Cli::parse();
@@ -203,35 +134,30 @@ fn main() -> Result<(), HiveUpError> {
let bzz_config = hive_config::read_config()?; let bzz_config = hive_config::read_config()?;
match args.mode { match args.mode {
HiveUpMode::Daemon => daemon::run(bzz_config, term_flag.clone(), reload_flag.clone())?, HiveUpMode::Boot(boot_submode) => match boot_submode {
HiveUpMode::Cli(cli_args) => { BootCommand::Update => {
if cli_args.upgrade {
cli_upgrade(bzz_config)?;
} else if cli_args.update_boot {
println!("Updating boot..."); println!("Updating boot...");
hive_system::boot_update()?; hive_system::boot_update()?;
} else if cli_args.init_boot_menu { }
BootCommand::EfiMenuInit => {
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());
} else if cli_args.init_boot { hive_system::init_efi_boot_record(&kernel_cmd)?;
}
BootCommand::Init => {
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()?;
} }
} },
HiveUpMode::Remote(remote_args) => { HiveUpMode::Pac(pac_mode) => match pac_mode {
if remote_args.check_updates { Pac::SnapshotSystem => {
let up_num = hive_intercom::client_check_for_updates()?; println!("Creating system snapshot...");
println!("Updates awailable: {}", up_num); let _ = hive_system::snapshot_system()?;
} else if remote_args.updates_avail { println!("Ok.");
let up_num = hive_intercom::client_updates_available()?;
println!("Updates awailable: {}", up_num);
} else if remote_args.schedule_update {
println!("Scheduling system update...");
hive_intercom::client_schedule_update()?;
print!("Done!")
} }
} }
} }