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