1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3#[cfg(feature = "connection-pool")]
4use std::marker::PhantomData;
5use std::path::Path;
6#[cfg(feature = "deadpool")]
7use std::path::PathBuf;
8
9use fcnet_types::{FirecrackerNetwork, FirecrackerNetworkOperation};
10use serde::Serialize;
11use socket::Socket;
12
13const OK_RESPONSE: &str = "OK";
14
15pub mod socket;
16
17#[derive(Debug)]
18pub enum FcnetdError {
19 RequestWriteError(std::io::Error),
20 RequestSerializeError(serde_json::Error),
21 ResponseReadError(std::io::Error),
22 ConnectionClosed,
23 OperationFailed(String),
24}
25
26impl std::error::Error for FcnetdError {}
27
28impl std::fmt::Display for FcnetdError {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 FcnetdError::RequestWriteError(err) => write!(f, "Writing the request to the socket failed: {err}"),
32 FcnetdError::RequestSerializeError(err) => write!(f, "Serializing the request to JSON failed: {err}"),
33 FcnetdError::ResponseReadError(err) => write!(f, "Reading the response from the connection failed: {err}"),
34 FcnetdError::ConnectionClosed => write!(f, "The connection was closed before a response could be received"),
35 FcnetdError::OperationFailed(detail) => {
36 write!(f, "The daemon returned a failure of the requested operation: {detail}")
37 }
38 }
39 }
40}
41
42#[derive(Debug)]
43pub struct FcnetdConnection<S: Socket>(S);
44
45#[derive(Serialize)]
46struct Request<'net> {
47 operation: FirecrackerNetworkOperation,
48 network: &'net FirecrackerNetwork,
49}
50
51#[derive(Debug)]
52#[cfg(feature = "connection-pool")]
53#[cfg_attr(docsrs, doc(cfg(feature = "connection-pool")))]
54pub struct FcnetdConnectionPool<S: Socket> {
55 path: PathBuf,
56 password: Option<String>,
57 phantom: PhantomData<S>,
58}
59
60#[cfg(feature = "connection-pool")]
61#[cfg_attr(docsrs, doc(cfg(feature = "connection-pool")))]
62impl<S: Socket> FcnetdConnectionPool<S> {
63 pub fn new(path: impl Into<PathBuf>) -> Self {
64 Self {
65 path: path.into(),
66 password: None,
67 phantom: PhantomData,
68 }
69 }
70
71 pub fn new_with_password(path: impl Into<PathBuf>, password: impl Into<String>) -> Self {
72 Self {
73 path: path.into(),
74 password: Some(password.into()),
75 phantom: PhantomData,
76 }
77 }
78}
79
80#[cfg(feature = "deadpool")]
81#[cfg_attr(docsrs, doc(cfg(feature = "deadpool")))]
82impl<S: Socket> deadpool::managed::Manager for FcnetdConnectionPool<S> {
83 type Type = FcnetdConnection<S>;
84
85 type Error = std::io::Error;
86
87 async fn create(&self) -> Result<Self::Type, Self::Error> {
88 match self.password {
89 Some(ref password) => FcnetdConnection::connect_with_password(&self.path, password).await,
90 None => FcnetdConnection::connect(&self.path).await,
91 }
92 }
93
94 async fn recycle(
95 &self,
96 _obj: &mut Self::Type,
97 _metrics: &deadpool::managed::Metrics,
98 ) -> deadpool::managed::RecycleResult<Self::Error> {
99 deadpool::managed::RecycleResult::Ok(())
100 }
101}
102
103impl<S: Socket> FcnetdConnection<S> {
104 pub async fn connect(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
105 let socket = S::connect(path.as_ref()).await?;
106 Ok(Self(socket))
107 }
108
109 pub async fn connect_with_password(path: impl AsRef<Path>, password: impl Into<String>) -> Result<Self, std::io::Error> {
110 let password = password.into();
111 let mut socket = S::connect(path.as_ref()).await?;
112 socket.write_line(password).await?;
113 Ok(Self(socket))
114 }
115
116 pub async fn run(&mut self, network: &FirecrackerNetwork, operation: FirecrackerNetworkOperation) -> Result<(), FcnetdError> {
117 let request = Request { operation, network };
118 let request_json = serde_json::to_string(&request).map_err(FcnetdError::RequestSerializeError)?;
119 self.0
120 .write_line(request_json)
121 .await
122 .map_err(FcnetdError::RequestWriteError)?;
123
124 let response = match self.0.read_line().await {
125 Ok(Some(response)) => response,
126 Ok(None) => return Err(FcnetdError::ConnectionClosed),
127 Err(err) => return Err(FcnetdError::ResponseReadError(err)),
128 };
129
130 if response != OK_RESPONSE {
131 return Err(FcnetdError::OperationFailed(response));
132 }
133
134 Ok(())
135 }
136}