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