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