From 2b35a143da848cb5fa91fd41c031d1e604d1662b Mon Sep 17 00:00:00 2001 From: Echo Date: Sun, 3 May 2026 01:37:22 +0000 Subject: [PATCH] fix: implement actual system reboot via shutdown/systemctl commands - Fix reboot_system() to use shutdown -r +N for delayed reboots - Fix patches handler to call reboot_system() instead of just logging - Add CAP_SYS_BOOT capability to systemd service for LXC reboot support - Remove unused warn import from packages/mod.rs - Bump version to 0.3.1 --- .a0proj/audit.db | Bin 1458176 -> 1495040 bytes Cargo.lock | 2 +- Cargo.toml | 2 +- configs/linux-patch-api.service | 3 +++ debian/changelog | 8 ++++++++ src/api/handlers/patches.rs | 17 ++++++++++++++++- src/packages/mod.rs | 30 ++++++++++++++++++++---------- tasks/lessons.md | 18 ++++++++++++++++++ 8 files changed, 67 insertions(+), 13 deletions(-) diff --git a/.a0proj/audit.db b/.a0proj/audit.db index 0baf9eada2ed9db5ace8c1c25b65a38e4b2b6d36..510ab39d9a64b154e514314a357a93eb0ca5de30 100644 GIT binary patch delta 12967 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 M!szEqemgSue?E+70RR91 delta 309 zcmZoz5Z%xaIYF8=jDdkMWuk&TW7x)oCHx%bd~X>*B9nOp@-{aLr0`9iARxH;y__f` zO9bOfhRIwCMU(3kY?xRU0c8ytH@{Sr=3|Lq&t%xlXCS~1R2(>2&RlGBy?HAKO9W>l zP}sqW6DVvy*~3#{@q~YfS41Axqz4(h-A*fLIWSg@9NX zh()${MvGQo;b3B9W?)>+$UM1G;JZkpYJ^azwx}$FbfY-0Akfz=3=Go^cZoSn*E}vJ aKYh|JF"] description = "Secure remote package management API for Linux systems" diff --git a/configs/linux-patch-api.service b/configs/linux-patch-api.service index 41791c7..171ee54 100644 --- a/configs/linux-patch-api.service +++ b/configs/linux-patch-api.service @@ -18,6 +18,9 @@ RuntimeDirectoryMode=0755 # Security hardening NoNewPrivileges=true +# Allow reboot capability for scheduled reboots +CapabilityBoundingSet=CAP_SYS_BOOT +AmbientCapabilities=CAP_SYS_BOOT # ProtectSystem removed - package management requires write access to /usr, /etc, /lib # Network security provided by mTLS + IP whitelist ProtectHome=true diff --git a/debian/changelog b/debian/changelog index d887fb6..37a5aa0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +linux-patch-api (0.3.1-1) unstable; urgency=low + + * Fix reboot endpoint: Implement actual system reboot via shutdown/systemctl + * Fix patches handler: Call reboot_system() instead of just logging + * Add CAP_SYS_BOOT capability to systemd service for LXC reboot support + * Remove unused warn import + + -- Echo Sat, 02 May 2026 20:37:00 -0500 linux-patch-api (0.3.0-1) unstable; urgency=low * v0.3.0 beta release diff --git a/src/api/handlers/patches.rs b/src/api/handlers/patches.rs index 98e7e93..a00c0b2 100644 --- a/src/api/handlers/patches.rs +++ b/src/api/handlers/patches.rs @@ -139,7 +139,22 @@ pub async fn apply_patches( ), ) .await; - // In production, would trigger actual reboot via system handler + // Trigger actual reboot via system handler + match backend_clone.reboot_system(request.reboot_delay_seconds) { + Ok(_) => { + let _ = job_manager_clone + .add_job_log( + &job_id_clone, + "Reboot command executed".to_string(), + ) + .await; + } + Err(e) => { + let _ = job_manager_clone + .add_job_log(&job_id_clone, format!("Reboot failed: {}", e)) + .await; + } + } } } Err(e) => { diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 2d89c08..b3a712d 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -6,7 +6,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::process::Command; -use tracing::{info, warn}; +use tracing::info; /// Package status #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -466,17 +466,27 @@ impl PackageManagerBackend for AptBackend { fn reboot_system(&self, delay_seconds: u64) -> Result<()> { if delay_seconds > 0 { - info!("Scheduling reboot in {} seconds", delay_seconds); - // In production, would use systemd shutdown scheduler - warn!("Delayed reboot not fully implemented - would use systemd in production"); + // Use shutdown command for delayed reboot (converts seconds to minutes, minimum 1) + let delay_minutes = std::cmp::max(1u64, delay_seconds.div_ceil(60)); + info!( + "Scheduling system reboot in {} minutes (requested {} seconds)", + delay_minutes, delay_seconds + ); + Command::new("shutdown") + .args(["-r", &format!("+{}", delay_minutes)]) + .status() + .context("Failed to schedule delayed reboot")?; + info!("System reboot scheduled in {} minutes", delay_minutes); + } else { + // Immediate reboot using systemctl + info!("Initiating immediate system reboot"); + Command::new("systemctl") + .arg("reboot") + .status() + .context("Failed to execute reboot command")?; + info!("System reboot initiated"); } - Command::new("systemctl") - .arg("reboot") - .status() - .context("Failed to execute reboot command")?; - - info!("System reboot initiated"); Ok(()) } } diff --git a/tasks/lessons.md b/tasks/lessons.md index d80c121..32ecd86 100644 --- a/tasks/lessons.md +++ b/tasks/lessons.md @@ -29,3 +29,21 @@ **Correction:** Always verify binary versions match before testing. Different BuildIDs mean different code. **Rule:** Check binary versions (file size, BuildID, --version output) on all target systems before testing. **Status:** Active + +## 2026-05-02 - Always run cargo fmt AND cargo clippy locally before pushing +**Mistake:** Pushed code changes without running cargo fmt and cargo clippy locally, causing 8 CI iterations to fix formatting and lint errors. +**Correction:** Run `cargo fmt --all -- --check` and `cargo clippy --all-targets --all-features -- -D warnings` locally before every push. +**Rule:** ALWAYS run cargo fmt AND cargo clippy locally before pushing to Gitea. Fix all errors before pushing. +**Status:** Active + +## 2026-05-02 - rustls 0.23 API: builder() vs builder_with_provider() +**Mistake:** Used ServerConfig::builder() which returns WantsVerifier state, then called with_protocol_versions() which requires WantsVersions state. +**Correction:** Use ServerConfig::builder_with_provider(Arc::new(aws_lc_rs::default_provider())) to get WantsVersions state. Also need aws_lc_rs feature in Cargo.toml. +**Rule:** In rustls 0.23, to set protocol versions, use builder_with_provider() not builder(). The builder() shortcut skips version negotiation. +**Status:** Active + +## 2026-05-02 - apt broken deps block unrelated package installs +**Mistake:** CI failed because openssh-server on runner had version mismatch (13.16 server vs 13.15 client), blocking all apt-get install operations. +**Correction:** Add `sudo apt-get -f install -y` before `sudo apt-get install` in CI workflow to fix broken deps automatically. +**Rule:** Always add `apt-get -f install -y` before `apt-get install` in CI workflows. Runners may have broken apt state from partial upgrades. +**Status:** Active