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 { handle_request( &self, req: &Request, api_notifier: EventFd, api_sender: Sender<ApiRequest>, ) -> Response31 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 { handle_request( &'static self, _api_notifier: EventFd, _api_sender: Sender<ApiRequest>, ) -> std::result::Result<Option<Body>, HttpError>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 { handle_request( &'static self, _api_notifier: EventFd, _api_sender: Sender<ApiRequest>, _body: &Option<Body>, _files: Vec<File>, ) -> std::result::Result<Option<Body>, HttpError>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 { handle_request( &'static self, api_notifier: EventFd, api_sender: Sender<ApiRequest>, body: &Option<Body>, mut files: Vec<File>, ) -> std::result::Result<Option<Body>, HttpError>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 { handle_request( &'static self, api_notifier: EventFd, api_sender: Sender<ApiRequest>, body: &Option<Body>, _files: Vec<File>, ) -> std::result::Result<Option<Body>, HttpError>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 { handle_request( &'static self, api_notifier: EventFd, api_sender: Sender<ApiRequest>, body: &Option<Body>, mut files: Vec<File>, ) -> std::result::Result<Option<Body>, HttpError>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 { new(action: &'static dyn HttpVmAction) -> Self305 pub fn new(action: &'static dyn HttpVmAction) -> Self { 306 VmActionHandler { action } 307 } 308 } 309 310 impl EndpointHandler for VmActionHandler { put_handler( &self, api_notifier: EventFd, api_sender: Sender<ApiRequest>, body: &Option<Body>, files: Vec<File>, ) -> std::result::Result<Option<Body>, HttpError>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 get_handler( &self, api_notifier: EventFd, api_sender: Sender<ApiRequest>, _body: &Option<Body>, ) -> std::result::Result<Option<Body>, HttpError>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 { handle_request( &self, req: &Request, api_notifier: EventFd, api_sender: Sender<ApiRequest>, ) -> Response335 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 { handle_request( &self, req: &Request, api_notifier: EventFd, api_sender: Sender<ApiRequest>, ) -> Response364 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 { handle_request( &self, req: &Request, api_notifier: EventFd, api_sender: Sender<ApiRequest>, ) -> Response394 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