pub(crate) async fn nudge_daemon_reload(capsule_ids: &[String]) {
use astrid_core::kernel_api::{KernelRequest, KernelResponse};
if capsule_ids.is_empty() {
return;
}
if !crate::socket_client::proxy_socket_path().exists() {
return;
}
let session_uuid = uuid::Uuid::new_v4();
let session = astrid_core::SessionId::from_uuid(session_uuid);
let Ok(mut client) =
crate::socket_client::SocketClient::connect(session, crate::principal::current()).await
else {
return;
};
for id in capsule_ids {
let Ok(val) = serde_json::to_value(KernelRequest::ReloadCapsule { id: id.clone() }) else {
continue;
};
let correlation = uuid::Uuid::new_v4().simple().to_string();
let request_topic = astrid_types::Topic::reload_capsule_request(&correlation);
let response_topic = astrid_types::Topic::reload_capsule_response(&correlation);
let msg = astrid_types::ipc::IpcMessage::new(
request_topic,
astrid_types::ipc::IpcPayload::RawJson(val),
session_uuid,
);
if client.send_message(msg).await.is_err() {
continue;
}
let Ok(raw) = client
.read_until_topic(response_topic.as_str(), std::time::Duration::from_secs(15))
.await
else {
eprintln!(
"Note: installed '{id}' to disk, but the running daemon didn't confirm a live reload in time — it will load on the next daemon start."
);
continue;
};
match crate::socket_client::SocketClient::extract_kernel_response(&raw) {
Some(KernelResponse::Success(_)) => {
eprintln!("Live: the running daemon loaded '{id}' — no restart needed.");
},
Some(KernelResponse::Error(reason)) => {
eprintln!(
"Note: installed '{id}' to disk, but the daemon declined a live reload ({reason}); it will load on the next daemon start."
);
},
_ => {
eprintln!(
"Note: installed '{id}' to disk, but couldn't confirm a live reload; it will load on the next daemon start."
);
},
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum LiveUnload {
NoDaemon,
NotLoaded,
Unloaded,
}
pub(crate) async fn try_daemon_unload(capsule_id: &str) -> anyhow::Result<LiveUnload> {
use anyhow::{Context, bail};
use astrid_core::kernel_api::{KernelRequest, KernelResponse};
if capsule_id.is_empty() || !crate::socket_client::proxy_socket_path().exists() {
return Ok(LiveUnload::NoDaemon);
}
let session_uuid = uuid::Uuid::new_v4();
let session = astrid_core::SessionId::from_uuid(session_uuid);
let Ok(mut client) =
crate::socket_client::SocketClient::connect(session, crate::principal::current()).await
else {
return Ok(LiveUnload::NoDaemon);
};
let val = serde_json::to_value(KernelRequest::UnloadCapsule {
id: capsule_id.to_string(),
})
.context("failed to encode live capsule unload request")?;
let correlation = uuid::Uuid::new_v4().simple().to_string();
let request_topic =
astrid_types::Topic::kernel_request(format!("unload_capsule.{correlation}"));
let response_topic =
astrid_types::Topic::kernel_response(format!("unload_capsule.{correlation}"));
let msg = astrid_types::ipc::IpcMessage::new(
request_topic,
astrid_types::ipc::IpcPayload::RawJson(val),
session_uuid,
);
client
.send_message(msg)
.await
.context("failed to send live capsule unload request")?;
let raw = client
.read_until_topic(response_topic.as_str(), std::time::Duration::from_secs(15))
.await
.context("running daemon did not confirm live capsule unload")?;
match crate::socket_client::SocketClient::extract_kernel_response(&raw) {
Some(KernelResponse::Success(data)) => {
match data.get("status").and_then(serde_json::Value::as_str) {
Some("unloaded") => Ok(LiveUnload::Unloaded),
Some("not_loaded") => Ok(LiveUnload::NotLoaded),
Some(other) => bail!("running daemon returned unknown unload status {other:?}"),
None => bail!("running daemon returned unload success without a status"),
}
},
Some(KernelResponse::Error(reason)) => {
bail!("running daemon declined live capsule unload: {reason}")
},
_ => bail!("running daemon returned a malformed live capsule unload response"),
}
}