xref: /cloud-hypervisor/vmm/src/api/http/http_endpoint.rs (revision eeae63b4595fbf0cc69f62b6e9d9a79c543c4ac7)
1 // Copyright © 2019 Intel Corporation
2 // Copyright 2024 Alyssa Ross <hi@alyssa.is>
3 //
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 
7 use std::fs::File;
8 use std::os::unix::io::IntoRawFd;
9 use std::sync::mpsc::Sender;
10 
11 use micro_http::{Body, Method, Request, Response, StatusCode, Version};
12 use vmm_sys_util::eventfd::EventFd;
13 
14 use crate::api::http::{error_response, EndpointHandler, HttpError};
15 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
16 use crate::api::VmCoredump;
17 use crate::api::{
18     AddDisk, ApiAction, ApiRequest, NetConfig, VmAddDevice, VmAddFs, VmAddNet, VmAddPmem,
19     VmAddUserDevice, VmAddVdpa, VmAddVsock, VmBoot, VmConfig, VmCounters, VmDelete, VmNmi, VmPause,
20     VmPowerButton, VmReboot, VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeZone, VmRestore,
21     VmResume, VmSendMigration, VmShutdown, VmSnapshot,
22 };
23 use crate::config::RestoreConfig;
24 
25 // /api/v1/vm.create handler
26 pub struct VmCreate {}
27 
28 impl EndpointHandler for VmCreate {
29     fn handle_request(
30         &self,
31         req: &Request,
32         api_notifier: EventFd,
33         api_sender: Sender<ApiRequest>,
34     ) -> Response {
35         match req.method() {
36             Method::Put => {
37                 match &req.body {
38                     Some(body) => {
39                         // Deserialize into a VmConfig
40                         let mut vm_config: Box<VmConfig> = match serde_json::from_slice(body.raw())
41                             .map_err(HttpError::SerdeJsonDeserialize)
42                         {
43                             Ok(config) => config,
44                             Err(e) => return error_response(e, StatusCode::BadRequest),
45                         };
46 
47                         if let Some(ref mut nets) = vm_config.net {
48                             if nets.iter().any(|net| net.fds.is_some()) {
49                                 warn!("Ignoring FDs sent via the HTTP request body");
50                             }
51                             for net in nets {
52                                 net.fds = None;
53                             }
54                         }
55 
56                         match crate::api::VmCreate
57                             .send(api_notifier, api_sender, vm_config)
58                             .map_err(HttpError::ApiError)
59                         {
60                             Ok(_) => Response::new(Version::Http11, StatusCode::NoContent),
61                             Err(e) => error_response(e, StatusCode::InternalServerError),
62                         }
63                     }
64 
65                     None => Response::new(Version::Http11, StatusCode::BadRequest),
66                 }
67             }
68 
69             _ => error_response(HttpError::BadRequest, StatusCode::BadRequest),
70         }
71     }
72 }
73 
74 pub trait GetHandler {
75     fn handle_request(
76         &'static self,
77         _api_notifier: EventFd,
78         _api_sender: Sender<ApiRequest>,
79     ) -> std::result::Result<Option<Body>, HttpError> {
80         Err(HttpError::BadRequest)
81     }
82 }
83 
84 pub trait PutHandler {
85     fn handle_request(
86         &'static self,
87         _api_notifier: EventFd,
88         _api_sender: Sender<ApiRequest>,
89         _body: &Option<Body>,
90         _files: Vec<File>,
91     ) -> std::result::Result<Option<Body>, HttpError> {
92         Err(HttpError::BadRequest)
93     }
94 }
95 
96 pub trait HttpVmAction: GetHandler + PutHandler + Sync {}
97 
98 impl<T: GetHandler + PutHandler + Sync> HttpVmAction for T {}
99 
100 macro_rules! vm_action_get_handler {
101     ($action:ty) => {
102         impl GetHandler for $action {
103             fn handle_request(
104                 &'static self,
105                 api_notifier: EventFd,
106                 api_sender: Sender<ApiRequest>,
107             ) -> std::result::Result<Option<Body>, HttpError> {
108                 self.send(api_notifier, api_sender, ())
109                     .map_err(HttpError::ApiError)
110             }
111         }
112 
113         impl PutHandler for $action {}
114     };
115 }
116 
117 macro_rules! vm_action_put_handler {
118     ($action:ty) => {
119         impl PutHandler for $action {
120             fn handle_request(
121                 &'static self,
122                 api_notifier: EventFd,
123                 api_sender: Sender<ApiRequest>,
124                 body: &Option<Body>,
125                 _files: Vec<File>,
126             ) -> std::result::Result<Option<Body>, HttpError> {
127                 if body.is_some() {
128                     Err(HttpError::BadRequest)
129                 } else {
130                     self.send(api_notifier, api_sender, ())
131                         .map_err(HttpError::ApiError)
132                 }
133             }
134         }
135 
136         impl GetHandler for $action {}
137     };
138 }
139 
140 macro_rules! vm_action_put_handler_body {
141     ($action:ty) => {
142         impl PutHandler for $action {
143             fn handle_request(
144                 &'static self,
145                 api_notifier: EventFd,
146                 api_sender: Sender<ApiRequest>,
147                 body: &Option<Body>,
148                 _files: Vec<File>,
149             ) -> std::result::Result<Option<Body>, HttpError> {
150                 if let Some(body) = body {
151                     self.send(
152                         api_notifier,
153                         api_sender,
154                         serde_json::from_slice(body.raw())?,
155                     )
156                     .map_err(HttpError::ApiError)
157                 } else {
158                     Err(HttpError::BadRequest)
159                 }
160             }
161         }
162 
163         impl GetHandler for $action {}
164     };
165 }
166 
167 vm_action_get_handler!(VmCounters);
168 
169 vm_action_put_handler!(VmBoot);
170 vm_action_put_handler!(VmDelete);
171 vm_action_put_handler!(VmShutdown);
172 vm_action_put_handler!(VmReboot);
173 vm_action_put_handler!(VmPause);
174 vm_action_put_handler!(VmResume);
175 vm_action_put_handler!(VmPowerButton);
176 vm_action_put_handler!(VmNmi);
177 
178 vm_action_put_handler_body!(VmAddDevice);
179 vm_action_put_handler_body!(AddDisk);
180 vm_action_put_handler_body!(VmAddFs);
181 vm_action_put_handler_body!(VmAddPmem);
182 vm_action_put_handler_body!(VmAddVdpa);
183 vm_action_put_handler_body!(VmAddVsock);
184 vm_action_put_handler_body!(VmAddUserDevice);
185 vm_action_put_handler_body!(VmRemoveDevice);
186 vm_action_put_handler_body!(VmResize);
187 vm_action_put_handler_body!(VmResizeZone);
188 vm_action_put_handler_body!(VmSnapshot);
189 vm_action_put_handler_body!(VmReceiveMigration);
190 vm_action_put_handler_body!(VmSendMigration);
191 
192 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
193 vm_action_put_handler_body!(VmCoredump);
194 
195 impl PutHandler for VmAddNet {
196     fn handle_request(
197         &'static self,
198         api_notifier: EventFd,
199         api_sender: Sender<ApiRequest>,
200         body: &Option<Body>,
201         mut files: Vec<File>,
202     ) -> std::result::Result<Option<Body>, HttpError> {
203         if let Some(body) = body {
204             let mut net_cfg: NetConfig = serde_json::from_slice(body.raw())?;
205             if net_cfg.fds.is_some() {
206                 warn!("Ignoring FDs sent via the HTTP request body");
207                 net_cfg.fds = None;
208             }
209             if !files.is_empty() {
210                 let fds = files.drain(..).map(|f| f.into_raw_fd()).collect();
211                 net_cfg.fds = Some(fds);
212             }
213             self.send(api_notifier, api_sender, net_cfg)
214                 .map_err(HttpError::ApiError)
215         } else {
216             Err(HttpError::BadRequest)
217         }
218     }
219 }
220 
221 impl GetHandler for VmAddNet {}
222 
223 impl PutHandler for VmRestore {
224     fn handle_request(
225         &'static self,
226         api_notifier: EventFd,
227         api_sender: Sender<ApiRequest>,
228         body: &Option<Body>,
229         mut files: Vec<File>,
230     ) -> std::result::Result<Option<Body>, HttpError> {
231         if let Some(body) = body {
232             let mut restore_cfg: RestoreConfig = serde_json::from_slice(body.raw())?;
233 
234             let mut fds = Vec::new();
235             if !files.is_empty() {
236                 fds = files.drain(..).map(|f| f.into_raw_fd()).collect();
237             }
238             let expected_fds = match restore_cfg.net_fds {
239                 Some(ref net_fds) => net_fds.iter().map(|net| net.num_fds).sum(),
240                 None => 0,
241             };
242             if fds.len() != expected_fds {
243                 error!(
244                     "Number of FDs expected: {}, but received: {}",
245                     expected_fds,
246                     fds.len()
247                 );
248                 return Err(HttpError::BadRequest);
249             }
250             if let Some(ref mut nets) = restore_cfg.net_fds {
251                 warn!("Ignoring FDs sent via the HTTP request body");
252                 let mut start_idx = 0;
253                 for restored_net in nets.iter_mut() {
254                     let end_idx = start_idx + restored_net.num_fds;
255                     restored_net.fds = Some(fds[start_idx..end_idx].to_vec());
256                     start_idx = end_idx;
257                 }
258             }
259 
260             self.send(api_notifier, api_sender, restore_cfg)
261                 .map_err(HttpError::ApiError)
262         } else {
263             Err(HttpError::BadRequest)
264         }
265     }
266 }
267 
268 impl GetHandler for VmRestore {}
269 
270 // Common handler for boot, shutdown and reboot
271 pub struct VmActionHandler {
272     action: &'static dyn HttpVmAction,
273 }
274 
275 impl VmActionHandler {
276     pub fn new(action: &'static dyn HttpVmAction) -> Self {
277         VmActionHandler { action }
278     }
279 }
280 
281 impl EndpointHandler for VmActionHandler {
282     fn put_handler(
283         &self,
284         api_notifier: EventFd,
285         api_sender: Sender<ApiRequest>,
286         body: &Option<Body>,
287         files: Vec<File>,
288     ) -> std::result::Result<Option<Body>, HttpError> {
289         PutHandler::handle_request(self.action, api_notifier, api_sender, body, files)
290     }
291 
292     fn get_handler(
293         &self,
294         api_notifier: EventFd,
295         api_sender: Sender<ApiRequest>,
296         _body: &Option<Body>,
297     ) -> std::result::Result<Option<Body>, HttpError> {
298         GetHandler::handle_request(self.action, api_notifier, api_sender)
299     }
300 }
301 
302 // /api/v1/vm.info handler
303 pub struct VmInfo {}
304 
305 impl EndpointHandler for VmInfo {
306     fn handle_request(
307         &self,
308         req: &Request,
309         api_notifier: EventFd,
310         api_sender: Sender<ApiRequest>,
311     ) -> Response {
312         match req.method() {
313             Method::Get => match crate::api::VmInfo
314                 .send(api_notifier, api_sender, ())
315                 .map_err(HttpError::ApiError)
316             {
317                 Ok(info) => {
318                     let mut response = Response::new(Version::Http11, StatusCode::OK);
319                     let info_serialized = serde_json::to_string(&info).unwrap();
320 
321                     response.set_body(Body::new(info_serialized));
322                     response
323                 }
324                 Err(e) => error_response(e, StatusCode::InternalServerError),
325             },
326             _ => error_response(HttpError::BadRequest, StatusCode::BadRequest),
327         }
328     }
329 }
330 
331 // /api/v1/vmm.info handler
332 pub struct VmmPing {}
333 
334 impl EndpointHandler for VmmPing {
335     fn handle_request(
336         &self,
337         req: &Request,
338         api_notifier: EventFd,
339         api_sender: Sender<ApiRequest>,
340     ) -> Response {
341         match req.method() {
342             Method::Get => match crate::api::VmmPing
343                 .send(api_notifier, api_sender, ())
344                 .map_err(HttpError::ApiError)
345             {
346                 Ok(pong) => {
347                     let mut response = Response::new(Version::Http11, StatusCode::OK);
348                     let info_serialized = serde_json::to_string(&pong).unwrap();
349 
350                     response.set_body(Body::new(info_serialized));
351                     response
352                 }
353                 Err(e) => error_response(e, StatusCode::InternalServerError),
354             },
355 
356             _ => error_response(HttpError::BadRequest, StatusCode::BadRequest),
357         }
358     }
359 }
360 
361 // /api/v1/vmm.shutdown handler
362 pub struct VmmShutdown {}
363 
364 impl EndpointHandler for VmmShutdown {
365     fn handle_request(
366         &self,
367         req: &Request,
368         api_notifier: EventFd,
369         api_sender: Sender<ApiRequest>,
370     ) -> Response {
371         match req.method() {
372             Method::Put => {
373                 match crate::api::VmmShutdown
374                     .send(api_notifier, api_sender, ())
375                     .map_err(HttpError::ApiError)
376                 {
377                     Ok(_) => Response::new(Version::Http11, StatusCode::OK),
378                     Err(e) => error_response(e, StatusCode::InternalServerError),
379                 }
380             }
381             _ => error_response(HttpError::BadRequest, StatusCode::BadRequest),
382         }
383     }
384 }
385