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