use std::io;
use shm_primitives::PeerId;
pub use shm_primitives::bootstrap::{
BOOTSTRAP_REQUEST_HEADER_LEN, BOOTSTRAP_REQUEST_MAGIC, BOOTSTRAP_RESPONSE_HEADER_LEN,
BOOTSTRAP_RESPONSE_MAGIC, BootstrapError, BootstrapRequestRef, BootstrapResponseOwned,
BootstrapResponseRef, BootstrapStatus, decode_request, decode_response, encode_request,
encode_response,
};
pub use shm_primitives::bootstrap::{
BootstrapSuccessNames, ReceivedBootstrapResponseStream, recv_response_stream,
send_response_stream,
};
use crate::host::{GuestSpawnTicket, HostHub, HostPeer};
use crate::segment::Segment;
pub use crate::host::PreparedPeer;
#[cfg(windows)]
pub use crate::host::guest_link_from_names;
#[cfg(unix)]
pub use crate::host::{guest_link_from_raw, guest_link_from_ticket};
pub struct PreparedBootstrapPeer {
pub host_peer: HostPeer,
pub guest_ticket: GuestSpawnTicket,
pub response_frame: Vec<u8>,
}
impl PreparedBootstrapPeer {
pub fn peer_id(&self) -> PeerId {
self.guest_ticket.peer_id
}
}
impl HostHub {
pub fn prepare_bootstrap_success(&self, payload: &[u8]) -> io::Result<PreparedBootstrapPeer> {
let prepared = self.prepare_peer()?;
let (host_peer, guest_ticket) = prepared.into_parts();
let response_frame = encode_response(
BootstrapStatus::Success,
guest_ticket.peer_id.get() as u32,
payload,
)
.map_err(|err| io::Error::other(format!("failed to encode bootstrap response: {err}")))?;
Ok(PreparedBootstrapPeer {
host_peer,
guest_ticket,
response_frame,
})
}
pub fn bootstrap_error_response(&self, message: &[u8]) -> io::Result<Vec<u8>> {
encode_response(BootstrapStatus::Error, 0, message)
.map_err(|err| io::Error::other(format!("failed to encode bootstrap error: {err}")))
}
}
#[cfg(unix)]
impl PreparedBootstrapPeer {
pub fn send_success_unix(
&self,
control_fd: std::os::fd::RawFd,
segment: &Segment,
) -> Result<(), BootstrapError> {
let fds = shm_primitives::bootstrap::BootstrapSuccessFds {
doorbell_fd: self.guest_ticket.doorbell.as_raw_fd(),
segment_fd: segment.as_raw_fd(),
mmap_control_fd: self.guest_ticket.mmap_rx.as_raw_fd(),
};
shm_primitives::bootstrap::send_response_unix(
control_fd,
BootstrapStatus::Success,
self.guest_ticket.peer_id.get() as u32,
decode_response(&self.response_frame)?.payload,
Some(&fds),
)
}
}
impl PreparedBootstrapPeer {
pub fn send_success_stream(
&self,
stream: &mut impl io::Write,
segment: &Segment,
) -> Result<(), BootstrapError> {
let names = BootstrapSuccessNames {
segment_path: segment
.path()
.to_str()
.ok_or_else(|| {
BootstrapError::Io(io::Error::new(
io::ErrorKind::InvalidData,
"segment path is not valid UTF-8",
))
})?
.to_string(),
doorbell_name: self.guest_ticket.doorbell_arg(),
mmap_ctrl_name: self.guest_ticket.mmap_rx_arg(),
};
let payload = names.encode();
send_response_stream(
stream,
BootstrapStatus::Success,
self.guest_ticket.peer_id.get() as u32,
&payload,
)
}
}