firecracker_rs_sdk/
firecracker.rs

1//! Option to launch firecracker
2
3use std::{
4    fs::{File, OpenOptions},
5    path::{Path, PathBuf},
6    process::{Command, Stdio},
7};
8
9use serde::{Deserialize, Serialize};
10
11use crate::{instance::Instance, Error, Result};
12
13pub const DEFAULT_API_SOCK: &'static str = "/run/firecracker.socket";
14pub const DEFAULT_HTTP_API_MAX_PAYLOAD_SIZE: usize = 51200;
15pub const DEFAULT_ID: &'static str = "anonymous-instance";
16
17#[derive(Debug, Clone, Default, Serialize, Deserialize)]
18pub struct FirecrackerOption {
19    firecracker_bin: PathBuf,
20
21    // Path to unix domain socket used by the API. [default: "/run/firecracker.socket"]
22    pub(crate) api_sock: Option<PathBuf>,
23
24    // Whether or not to load boot timer device for logging elapsed time since InstanceStart command.
25    boot_timer: Option<bool>,
26
27    // Path to a file that contains the microVM configuration in JSON format.
28    config_file: Option<PathBuf>,
29
30    // Print the data format version of the provided snapshot state file.
31    describe_snapshot: Option<bool>,
32
33    // Http API request payload max size, in bytes. [default: "51200"]
34    http_api_max_payload_size: Option<usize>,
35
36    // MicroVM unique identifier. [default: "anonymous-instance"]
37    id: Option<String>,
38
39    // Set the logger level.
40    level: Option<String>,
41
42    // Path to a fifo or a file used for configuring the logger on startup.
43    log_path: Option<PathBuf>,
44
45    // Path to a file that contains metadata in JSON format to add to the mmds.
46    metadata: Option<PathBuf>,
47
48    // Path to a fifo or a file used for configuring the metrics on startup.
49    metrics_path: Option<PathBuf>,
50
51    // Mmds data store limit, in bytes.
52    mmds_size_limit: Option<PathBuf>,
53
54    // Set the logger module filter.
55    module: Option<String>,
56
57    // Optional parameter which allows starting and using a microVM without an active API socket.
58    no_api: Option<bool>,
59
60    // Optional parameter which allows starting and using a microVM without seccomp filtering. Not recommended.
61    no_seccomp: Option<bool>,
62
63    // Parent process CPU time (wall clock, microseconds). This parameter is optional.
64    parent_cpu_time_us: Option<usize>,
65
66    // Optional parameter which allows specifying the path to a custom seccomp filter. For advanced users.
67    seccomp_filter: Option<String>,
68
69    // Whether or not to output the level in the logs.
70    show_level: Option<bool>,
71
72    // Whether or not to include the file path and line number of the log's origin.
73    show_log_origin: Option<bool>,
74
75    // Process start CPU time (wall clock, microseconds). This parameter is optional.
76    start_time_cpu_us: Option<usize>,
77
78    // Process start time (wall clock, microseconds). This parameter is optional.
79    start_time_us: Option<usize>,
80
81    // Stdin of the firecracker, ignored when using jailer.
82    stdin: Option<PathBuf>,
83
84    // Stdout of the firecracker, ignored when using jailer.
85    stdout: Option<PathBuf>,
86
87    // Stderr of the firecracker, ignored when using jailer.
88    stderr: Option<PathBuf>,
89}
90
91impl FirecrackerOption {
92    pub fn new<P: AsRef<Path>>(firecracker_bin: P) -> Self {
93        Self {
94            firecracker_bin: firecracker_bin.as_ref().into(),
95            ..Default::default()
96        }
97    }
98
99    fn exec_file_name(&self) -> Result<PathBuf> {
100        let exec_file_name = self
101            .firecracker_bin
102            .file_name()
103            .ok_or_else(|| Error::Configuration("jailer `exec_file` ends with `..`".into()))?;
104        Ok(exec_file_name.into())
105    }
106
107    pub fn build(&mut self) -> Result<Instance> {
108        // spawn instance directly with firecracker
109        let mut command = self.build_cmd();
110
111        // Redirect stdin, stdout and stderr
112        if let Some(ref stdin) = self.stdin {
113            command.stdin(Stdio::from(File::open(stdin)?));
114        }
115
116        if let Some(ref stdout) = self.stdout {
117            command.stdout(Stdio::from(
118                OpenOptions::new().create(true).write(true).open(stdout)?,
119            ));
120        }
121
122        if let Some(ref stderr) = self.stderr {
123            command.stderr(Stdio::from(
124                OpenOptions::new().create(true).write(true).open(stderr)?,
125            ));
126        }
127
128        let socket_on_host = self
129            .api_sock
130            .clone()
131            .unwrap_or_else(|| DEFAULT_API_SOCK.into());
132
133        Ok(Instance::new(
134            socket_on_host,
135            None,
136            None,
137            None,
138            command,
139            self.exec_file_name()?,
140        ))
141    }
142
143    pub(crate) fn build_cmd(&self) -> Command {
144        let mut cmd = Command::new(&self.firecracker_bin);
145
146        let api_sock = match self.api_sock {
147            Some(ref api_sock) => api_sock,
148            None => &DEFAULT_API_SOCK.into(),
149        };
150
151        // let api_sock = if let Some(ref jailer_workspace_dir) = jailer_workspace_dir {
152        //     &jailer_workspace_dir.join(api_sock)
153        // } else {
154        //     api_sock
155        // };
156
157        cmd.arg("--api-sock").arg(api_sock);
158
159        if let Some(true) = self.boot_timer {
160            cmd.arg("--boot-timer");
161        }
162
163        if let Some(ref config_file) = self.config_file {
164            cmd.arg("--config-file").arg(config_file);
165        }
166
167        if let Some(ref http_api_max_payload_size) = self.http_api_max_payload_size {
168            cmd.arg("--http-api-max-payload-size")
169                .arg(http_api_max_payload_size.to_string());
170        }
171
172        if let Some(ref id) = self.id {
173            cmd.arg("--id").arg(id);
174        }
175
176        if let Some(ref level) = self.level {
177            cmd.arg("--level").arg(level);
178        }
179
180        if let Some(ref log_path) = self.log_path {
181            cmd.arg("--log-path").arg(log_path);
182        }
183
184        if let Some(ref metadata) = self.metadata {
185            cmd.arg("--metadata").arg(metadata);
186        }
187
188        if let Some(ref metrics_path) = self.metrics_path {
189            cmd.arg("--metrics-path").arg(metrics_path);
190        }
191
192        if let Some(ref mmds_size_limit) = self.mmds_size_limit {
193            cmd.arg("--mmds-size-limit").arg(mmds_size_limit);
194        }
195
196        if let Some(ref module) = self.module {
197            cmd.arg("--module").arg(module);
198        }
199
200        if let Some(true) = self.no_api {
201            cmd.arg("--no-api");
202        }
203
204        if let Some(true) = self.no_seccomp {
205            cmd.arg("--no-seccomp");
206        }
207
208        if let Some(ref parent_cpu_time_us) = self.parent_cpu_time_us {
209            cmd.arg("--parent-cpu-time-us")
210                .arg(parent_cpu_time_us.to_string());
211        }
212
213        if let Some(ref seccomp_filter) = self.seccomp_filter {
214            cmd.arg("--seccomp-filter").arg(seccomp_filter);
215        }
216
217        if let Some(true) = self.show_level {
218            cmd.arg("--show-level");
219        }
220
221        if let Some(true) = self.show_log_origin {
222            cmd.arg("--show-log-origin");
223        }
224
225        if let Some(ref start_time_cpu_us) = self.start_time_cpu_us {
226            cmd.arg("--start-time-cpu-us")
227                .arg(start_time_cpu_us.to_string());
228        }
229
230        if let Some(ref start_time_us) = self.start_time_us {
231            cmd.arg("--start-time-us").arg(start_time_us.to_string());
232        }
233
234        cmd
235    }
236
237    pub fn api_sock<P: AsRef<Path>>(&mut self, api_sock: P) -> &mut Self {
238        self.api_sock = Some(api_sock.as_ref().into());
239        self
240    }
241
242    pub fn boot_timer(&mut self) -> &mut Self {
243        self.boot_timer = Some(true);
244        self
245    }
246
247    pub fn config_file<P: AsRef<Path>>(&mut self, config_file: Option<P>) -> &mut Self {
248        self.config_file = config_file.and_then(|x| Some(x.as_ref().to_path_buf()));
249        self
250    }
251
252    pub fn http_api_max_payload_size(
253        &mut self,
254        http_api_max_payload_size: usize,
255    ) -> &mut Self {
256        self.http_api_max_payload_size = Some(http_api_max_payload_size);
257        self
258    }
259
260    pub fn id<S: AsRef<str>>(&mut self, id: S) -> &mut Self {
261        self.id = Some(id.as_ref().to_string());
262        self
263    }
264
265    pub fn level<S: AsRef<str>>(&mut self, level: S) -> &mut Self {
266        self.level = Some(level.as_ref().to_string());
267        self
268    }
269
270    pub fn log_path<P: AsRef<Path>>(&mut self, log_path: Option<P>) -> &mut Self {
271        self.log_path = log_path.and_then(|x| Some(x.as_ref().to_path_buf()));
272        self
273    }
274
275    pub fn metadata<P: AsRef<Path>>(&mut self, metadata: Option<P>) -> &mut Self {
276        self.metadata = metadata.and_then(|x| Some(x.as_ref().to_path_buf()));
277        self
278    }
279
280    pub fn metrics_path<P: AsRef<Path>>(&mut self, metrics_path: Option<P>) -> &mut Self {
281        self.metrics_path = metrics_path.and_then(|x| Some(x.as_ref().to_path_buf()));
282        self
283    }
284
285    pub fn mmds_size_limit<P: AsRef<Path>>(&mut self, mmds_size_limit: Option<P>) -> &mut Self {
286        self.mmds_size_limit = mmds_size_limit.and_then(|x| Some(x.as_ref().to_path_buf()));
287        self
288    }
289
290    pub fn module<S: AsRef<str>>(&mut self, module: S) -> &mut Self {
291        self.module = Some(module.as_ref().to_string());
292        self
293    }
294
295    pub fn no_api(&mut self) -> &mut Self {
296        self.no_api = Some(true);
297        self
298    }
299
300    pub fn no_seccomp(&mut self) -> &mut Self {
301        self.no_seccomp = Some(true);
302        self
303    }
304
305    pub fn parent_cpu_time_us(&mut self, parent_cpu_time_us: usize) -> &mut Self {
306        self.parent_cpu_time_us = Some(parent_cpu_time_us);
307        self
308    }
309
310    pub fn seccomp_filter<S: AsRef<str>>(&mut self, seccomp_filter: S) -> &mut Self {
311        self.seccomp_filter = Some(seccomp_filter.as_ref().to_string());
312        self
313    }
314
315    pub fn show_level(&mut self) -> &mut Self {
316        self.show_level = Some(true);
317        self
318    }
319
320    pub fn show_log_origin(&mut self) -> &mut Self {
321        self.show_log_origin = Some(true);
322        self
323    }
324
325    pub fn start_time_cpu_us(&mut self, start_time_cpu_us: usize) -> &mut Self {
326        self.start_time_cpu_us = Some(start_time_cpu_us);
327        self
328    }
329
330    pub fn start_time_us(&mut self, start_time_us: usize) -> &mut Self {
331        self.start_time_us = Some(start_time_us);
332        self
333    }
334
335    pub fn stdin<P: AsRef<Path>>(&mut self, stdin: P) -> &mut Self {
336        self.stdin = Some(stdin.as_ref().into());
337        self
338    }
339
340    pub fn stdout<P: AsRef<Path>>(&mut self, stdout: P) -> &mut Self {
341        self.stdout = Some(stdout.as_ref().into());
342        self
343    }
344
345    pub fn stderr<P: AsRef<Path>>(&mut self, stderr: P) -> &mut Self {
346        self.stderr = Some(stderr.as_ref().into());
347        self
348    }
349}