diff --git a/hive-c0re/src/bin/hivectl.rs b/hive-c0re/src/bin/hivectl.rs index eb70eaa5..f692cbc0 100644 --- a/hive-c0re/src/bin/hivectl.rs +++ b/hive-c0re/src/bin/hivectl.rs @@ -759,9 +759,9 @@ async fn main() -> Result<()> { resume_session, } => choom(&name, resume_session.as_deref()), Cmd::Quota { cmd } => match cmd { - QuotaCmd::Enable => quota_enable().await, + QuotaCmd::Enable => quota_enable(&socket).await, QuotaCmd::Show { name } => quota_show(name.as_deref()).await, - QuotaCmd::Limit { name, size } => quota_limit(&name, &size).await, + QuotaCmd::Limit { name, size } => quota_limit(&socket, &name, &size).await, }, Cmd::MarkdownDocs => { print!("{}", clap_markdown::help_markdown::()); @@ -1041,13 +1041,8 @@ fn wg_status() -> Result<()> { } /// `quota enable` — turn on btrfs qgroup accounting (operator opt-in). -async fn quota_enable() -> Result<()> { - hive_c0re::priv_client::ensure_btrfs_quota() - .await - .context("enable btrfs qgroup accounting")?; - println!("btrfs qgroup accounting enabled on the agent-state filesystem."); - println!("(usage may read 0 until btrfs finishes its background rescan)"); - Ok(()) +async fn quota_enable(socket: &Path) -> Result<()> { + daemon_request(socket, hive_host_sock::HostRequest::QuotaEnable, "quota").await } /// `quota show [name]` — report per-agent disk usage from btrfs qgroups. @@ -1088,11 +1083,17 @@ async fn quota_show(name: Option<&str>) -> Result<()> { } /// `quota limit ` — set or clear an agent's disk quota. -async fn quota_limit(name: &str, size: &str) -> Result<()> { +async fn quota_limit(socket: &Path, name: &str, size: &str) -> Result<()> { let limit = parse_quota_size(size)?; - hive_c0re::priv_client::set_subvolume_quota(name, limit) - .await - .with_context(|| format!("set quota for {name}"))?; + daemon_request( + socket, + hive_host_sock::HostRequest::QuotaLimit { + name: name.to_owned(), + limit, + }, + "quota", + ) + .await?; match limit { Some(n) => println!("set {name} quota to {} ({n} bytes)", human_bytes(n)), None => println!("cleared {name} quota"), diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index 8a184039..58da91a7 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -199,6 +199,8 @@ async fn dispatch(req: &HostRequest, coord: Arc) -> HostResponse { HostRequest::SetAgentGithubToken { agent, token } => { handle_set_agent_github_token(agent, token).await? } + HostRequest::QuotaEnable => handle_quota_enable().await?, + HostRequest::QuotaLimit { name, limit } => handle_quota_limit(name, *limit).await?, }) } .await; @@ -408,6 +410,25 @@ async fn handle_set_agent_github_token(agent: &str, token: &str) -> Result Result { + crate::priv_client::ensure_btrfs_quota() + .await + .context("enable btrfs qgroup accounting")?; + Ok(HostResponse::messages(vec![ + "btrfs qgroup accounting enabled on the agent-state filesystem.".to_owned(), + "(usage may read 0 until btrfs finishes its background rescan)".to_owned(), + ])) +} + +async fn handle_quota_limit(name: &str, limit: Option) -> Result { + crate::priv_client::set_subvolume_quota(name, limit) + .await + .with_context(|| format!("set quota for {name}"))?; + // Bare success: the client prints the human-readable confirmation from + // the value it sent (it holds the `human_bytes` formatter). + Ok(HostResponse::success()) +} + async fn handle_matrix_sync_admin() -> Result { require_matrix_present().await?; let register_token = diff --git a/hive-host-sock/src/lib.rs b/hive-host-sock/src/lib.rs index 1f55db5e..d84d4752 100644 --- a/hive-host-sock/src/lib.rs +++ b/hive-host-sock/src/lib.rs @@ -164,6 +164,20 @@ pub enum HostRequest { /// (inline flag or stdin); the daemon just persists it. Read live by /// the agent's `gh` wrapper / git credential helper — no rebuild needed. SetAgentGithubToken { agent: String, token: String }, + /// Turn on btrfs qgroup accounting on the agent-state filesystem, via + /// the privileged helper. Daemon-side equivalent of `hivectl quota + /// enable`. Returns advisory lines in [`HostResponse::messages`]. + QuotaEnable, + /// Set (or, with `limit = None`, clear) an agent's btrfs disk quota via + /// the privileged helper. Daemon-side equivalent of `hivectl quota + /// limit`; the size string is parsed to bytes client-side. Returns a + /// bare success — the client prints the confirmation from the value it + /// sent. + QuotaLimit { + name: String, + #[serde(default)] + limit: Option, + }, } /// Selects which container classes a hive-wide [`HostRequest::Stop`] /