xref: /cloud-hypervisor/vmm/src/api/http/http_endpoint.rs (revision 3f8cd52ffd74627242cb7e8ea1c2bdedadf6741a)
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;
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!(VmRestore);
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 // Common handler for boot, shutdown and reboot
224 pub struct VmActionHandler {
225     action: &'static dyn HttpVmAction,
226 }
227 
228 impl VmActionHandler {
229     pub fn new(action: &'static dyn HttpVmAction) -> Self {
230         VmActionHandler { action }
231     }
232 }
233 
234 impl EndpointHandler for VmActionHandler {
235     fn put_handler(
236         &self,
237         api_notifier: EventFd,
238         api_sender: Sender<ApiRequest>,
239         body: &Option<Body>,
240         files: Vec<File>,
241     ) -> std::result::Result<Option<Body>, HttpError> {
242         PutHandler::handle_request(self.action, api_notifier, api_sender, body, files)
243     }
244 
245     fn get_handler(
246         &self,
247         api_notifier: EventFd,
248         api_sender: Sender<ApiRequest>,
249         _body: &Option<Body>,
250     ) -> std::result::Result<Option<Body>, HttpError> {
251         GetHandler::handle_request(self.action, api_notifier, api_sender)
252     }
253 }
254 
255 // /api/v1/vm.info handler
256 pub struct VmInfo {}
257 
258 impl EndpointHandler for VmInfo {
259     fn handle_request(
260         &self,
261         req: &Request,
262         api_notifier: EventFd,
263         api_sender: Sender<ApiRequest>,
264     ) -> Response {
265         match req.method() {
266             Method::Get => match crate::api::VmInfo
267                 .send(api_notifier, api_sender, ())
268                 .map_err(HttpError::ApiError)
269             {
270                 Ok(info) => {
271                     let mut response = Response::new(Version::Http11, StatusCode::OK);
272                     let info_serialized = serde_json::to_string(&info).unwrap();
273 
274                     response.set_body(Body::new(info_serialized));
275                     response
276                 }
277                 Err(e) => error_response(e, StatusCode::InternalServerError),
278             },
279             _ => error_response(HttpError::BadRequest, StatusCode::BadRequest),
280         }
281     }
282 }
283 
284 // /api/v1/vmm.info handler
285 pub struct VmmPing {}
286 
287 impl EndpointHandler for VmmPing {
288     fn handle_request(
289         &self,
290         req: &Request,
291         api_notifier: EventFd,
292         api_sender: Sender<ApiRequest>,
293     ) -> Response {
294         match req.method() {
295             Method::Get => match crate::api::VmmPing
296                 .send(api_notifier, api_sender, ())
297                 .map_err(HttpError::ApiError)
298             {
299                 Ok(pong) => {
300                     let mut response = Response::new(Version::Http11, StatusCode::OK);
301                     let info_serialized = serde_json::to_string(&pong).unwrap();
302 
303                     response.set_body(Body::new(info_serialized));
304                     response
305                 }
306                 Err(e) => error_response(e, StatusCode::InternalServerError),
307             },
308 
309             _ => error_response(HttpError::BadRequest, StatusCode::BadRequest),
310         }
311     }
312 }
313 
314 // /api/v1/vmm.shutdown handler
315 pub struct VmmShutdown {}
316 
317 impl EndpointHandler for VmmShutdown {
318     fn handle_request(
319         &self,
320         req: &Request,
321         api_notifier: EventFd,
322         api_sender: Sender<ApiRequest>,
323     ) -> Response {
324         match req.method() {
325             Method::Put => {
326                 match crate::api::VmmShutdown
327                     .send(api_notifier, api_sender, ())
328                     .map_err(HttpError::ApiError)
329                 {
330                     Ok(_) => Response::new(Version::Http11, StatusCode::OK),
331                     Err(e) => error_response(e, StatusCode::InternalServerError),
332                 }
333             }
334             _ => error_response(HttpError::BadRequest, StatusCode::BadRequest),
335         }
336     }
337 }
338