1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Contains the multi-process broker for crosvm. This is a work in progress, and some example
6 //! structs here are dead code.
7 #![allow(dead_code)]
8 use std::boxed::Box;
9 use std::collections::HashMap;
10 use std::env::current_exe;
11 use std::ffi::OsStr;
12 use std::fmt;
13 use std::fmt::Debug;
14 use std::fmt::Display;
15 use std::fmt::Formatter;
16 use std::fs::OpenOptions;
17 use std::os::windows::io::AsRawHandle;
18 use std::os::windows::io::RawHandle;
19 use std::path::Path;
20 use std::path::PathBuf;
21 use std::process;
22 use std::process::Command;
23 use std::time::Duration;
24
25 use anyhow::anyhow;
26 use anyhow::Context;
27 use anyhow::Result;
28 use base::enable_high_res_timers;
29 use base::error;
30 #[cfg(feature = "crash-report")]
31 use base::generate_uuid;
32 use base::info;
33 use base::named_pipes;
34 use base::syslog;
35 use base::syslog::LogArgs;
36 use base::syslog::LogConfig;
37 use base::warn;
38 use base::AsRawDescriptor;
39 use base::BlockingMode;
40 use base::Descriptor;
41 use base::DuplicateHandleRequest;
42 use base::DuplicateHandleResponse;
43 use base::Event;
44 use base::EventToken;
45 use base::FramingMode;
46 use base::RawDescriptor;
47 use base::ReadNotifier;
48 use base::RecvTube;
49 use base::SafeDescriptor;
50 use base::SendTube;
51 #[cfg(feature = "gpu")]
52 use base::StreamChannel;
53 use base::Timer;
54 use base::TimerTrait;
55 use base::Tube;
56 use base::WaitContext;
57 #[cfg(feature = "process-invariants")]
58 use broker_ipc::init_broker_process_invariants;
59 use broker_ipc::CommonChildStartupArgs;
60 #[cfg(feature = "process-invariants")]
61 use broker_ipc::EmulatorProcessInvariants;
62 #[cfg(feature = "crash-report")]
63 use crash_report::product_type;
64 #[cfg(feature = "crash-report")]
65 use crash_report::CrashReportAttributes;
66 use crosvm_cli::bail_exit_code;
67 use crosvm_cli::ensure_exit_code;
68 use crosvm_cli::sys::windows::exit::to_process_type_error;
69 use crosvm_cli::sys::windows::exit::Exit;
70 use crosvm_cli::sys::windows::exit::ExitCode;
71 use crosvm_cli::sys::windows::exit::ExitCodeWrapper;
72 use crosvm_cli::sys::windows::exit::ExitContext;
73 use crosvm_cli::sys::windows::exit::ExitContextAnyhow;
74 #[cfg(feature = "audio")]
75 use devices::virtio::snd::parameters::Parameters as SndParameters;
76 #[cfg(feature = "gpu")]
77 use devices::virtio::vhost::user::device::gpu::sys::windows::GpuBackendConfig;
78 #[cfg(feature = "gpu")]
79 use devices::virtio::vhost::user::device::gpu::sys::windows::GpuVmmConfig;
80 #[cfg(feature = "gpu")]
81 use devices::virtio::vhost::user::device::gpu::sys::windows::InputEventBackendConfig;
82 #[cfg(feature = "gpu")]
83 use devices::virtio::vhost::user::device::gpu::sys::windows::InputEventSplitConfig;
84 #[cfg(feature = "gpu")]
85 use devices::virtio::vhost::user::device::gpu::sys::windows::InputEventVmmConfig;
86 #[cfg(feature = "gpu")]
87 use devices::virtio::vhost::user::device::gpu::sys::windows::WindowProcedureThreadSplitConfig;
88 #[cfg(feature = "gpu")]
89 use devices::virtio::vhost::user::device::gpu::sys::windows::WindowProcedureThreadVmmConfig;
90 #[cfg(feature = "audio")]
91 use devices::virtio::vhost::user::device::snd::sys::windows::SndBackendConfig;
92 #[cfg(feature = "audio")]
93 use devices::virtio::vhost::user::device::snd::sys::windows::SndSplitConfig;
94 #[cfg(feature = "audio")]
95 use devices::virtio::vhost::user::device::snd::sys::windows::SndVmmConfig;
96 #[cfg(feature = "net")]
97 use devices::virtio::vhost::user::device::NetBackendConfig;
98 use devices::virtio::DeviceType;
99 #[cfg(feature = "gpu")]
100 use gpu_display::EventDevice;
101 #[cfg(feature = "gpu")]
102 use gpu_display::WindowProcedureThread;
103 #[cfg(feature = "gpu")]
104 use gpu_display::WindowProcedureThreadBuilder;
105 use metrics::MetricEventType;
106 #[cfg(all(feature = "net", feature = "slirp"))]
107 use net_util::slirp::sys::windows::SlirpStartupConfig;
108 #[cfg(all(feature = "net", feature = "slirp"))]
109 use net_util::slirp::sys::windows::SLIRP_BUFFER_SIZE;
110 use serde::Deserialize;
111 use serde::Serialize;
112 use tube_transporter::TubeToken;
113 use tube_transporter::TubeTransferData;
114 use tube_transporter::TubeTransporter;
115 use win_util::get_exit_code_process;
116 use win_util::ProcessType;
117 use winapi::shared::winerror::ERROR_ACCESS_DENIED;
118 use winapi::um::processthreadsapi::TerminateProcess;
119
120 use crate::crosvm::config::InputDeviceOption;
121 #[cfg(feature = "gpu")]
122 use crate::sys::windows::get_gpu_product_configs;
123 #[cfg(feature = "audio")]
124 use crate::sys::windows::get_snd_product_configs;
125 #[cfg(feature = "gpu")]
126 use crate::sys::windows::get_window_procedure_thread_product_configs;
127 #[cfg(feature = "audio")]
128 use crate::sys::windows::num_input_sound_devices;
129 #[cfg(feature = "audio")]
130 use crate::sys::windows::num_input_sound_streams;
131 use crate::Config;
132
133 const KILL_CHILD_EXIT_CODE: u32 = 1;
134
135 /// Tubes created by the broker and sent to child processes via the bootstrap tube.
136 #[derive(Serialize, Deserialize)]
137 pub struct BrokerTubes {
138 pub vm_evt_wrtube: SendTube,
139 pub vm_evt_rdtube: RecvTube,
140 }
141
142 /// This struct represents a configured "disk" device as returned by the platform's API. There will
143 /// be two instances of it for each disk device, with the Tubes connected appropriately. The broker
144 /// will send one of these to the main process, and the other to the vhost user disk backend.
145 struct DiskDeviceEnd {
146 bootstrap_tube: Tube,
147 vhost_user: Tube,
148 }
149
150 /// Example of the function that would be in linux.rs.
platform_create_disks(_cfg: Config) -> Vec<(DiskDeviceEnd, DiskDeviceEnd)>151 fn platform_create_disks(_cfg: Config) -> Vec<(DiskDeviceEnd, DiskDeviceEnd)> {
152 unimplemented!()
153 }
154
155 /// Time to wait after a process failure for the remaining processes to exit. When exceeded, all
156 /// remaining processes, except metrics, will be terminated.
157 const EXIT_TIMEOUT: Duration = Duration::from_secs(3);
158 /// Time to wait for the metrics process to flush and upload all logs.
159 const METRICS_TIMEOUT: Duration = Duration::from_secs(3);
160
161 /// DLLs that are known to interfere with crosvm.
162 #[cfg(feature = "sandbox")]
163 const BLOCKLIST_DLLS: &[&str] = &[
164 "action_x64.dll",
165 "AudioDevProps2.dll",
166 "GridWndHook.dll",
167 "Nahimic2OSD.dll",
168 "NahimicOSD.dll",
169 "TwitchNativeOverlay64.dll",
170 "XSplitGameSource64.dll",
171 "SS2OSD.dll",
172 "nhAsusStrixOSD.dll",
173 ];
174
175 /// Maps a process type to its sandbox policy configuration.
176 #[cfg(feature = "sandbox")]
process_policy(process_type: ProcessType, cfg: &Config) -> sandbox::policy::Policy177 fn process_policy(process_type: ProcessType, cfg: &Config) -> sandbox::policy::Policy {
178 #[allow(unused_mut)]
179 let mut policy = match process_type {
180 ProcessType::Block => sandbox::policy::BLOCK,
181 ProcessType::Main => main_process_policy(cfg),
182 ProcessType::Metrics => sandbox::policy::METRICS,
183 ProcessType::Net => sandbox::policy::NET,
184 ProcessType::Slirp => slirp_process_policy(cfg),
185 ProcessType::Gpu => sandbox::policy::GPU,
186 ProcessType::Snd => sandbox::policy::SND,
187 ProcessType::Broker => unimplemented!("No broker policy"),
188 ProcessType::Spu => unimplemented!("No SPU policy"),
189 };
190
191 for dll in BLOCKLIST_DLLS.iter() {
192 policy.dll_blocklist.push(dll.to_string());
193 }
194
195 #[cfg(feature = "asan")]
196 adjust_asan_policy(&mut policy);
197 #[cfg(feature = "cperfetto")]
198 adjust_perfetto_policy(&mut policy);
199 policy
200 }
201
202 /// Dynamically appends rules to the main process's policy.
203 #[cfg(feature = "sandbox")]
main_process_policy(cfg: &Config) -> sandbox::policy::Policy204 fn main_process_policy(cfg: &Config) -> sandbox::policy::Policy {
205 let mut policy = sandbox::policy::MAIN;
206 if let Some(host_guid) = &cfg.host_guid {
207 let rule = sandbox::policy::Rule {
208 subsystem: sandbox::SubSystem::SUBSYS_FILES,
209 semantics: sandbox::Semantics::FILES_ALLOW_ANY,
210 pattern: format!("\\??\\pipe\\{}\\vsock-*", host_guid),
211 };
212 policy.exceptions.push(rule);
213 }
214 policy
215 }
216
217 #[cfg(feature = "sandbox")]
slirp_process_policy(#[allow(unused)] cfg: &Config) -> sandbox::policy::Policy218 fn slirp_process_policy(#[allow(unused)] cfg: &Config) -> sandbox::policy::Policy {
219 #[allow(unused_mut)]
220 let mut policy = sandbox::policy::SLIRP;
221
222 #[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))]
223 if let Some(path) = &cfg.slirp_capture_file {
224 policy.exceptions.push(sandbox::policy::Rule {
225 subsystem: sandbox::SubSystem::SUBSYS_FILES,
226 semantics: sandbox::Semantics::FILES_ALLOW_ANY,
227 pattern: path.to_owned(),
228 });
229 }
230
231 policy
232 }
233
234 /// Adjust a policy to allow ASAN builds to write output files.
235 #[cfg(feature = "sandbox")]
adjust_asan_policy(policy: &mut sandbox::policy::Policy)236 fn adjust_asan_policy(policy: &mut sandbox::policy::Policy) {
237 if (policy.initial_token_level as i32) < (sandbox::TokenLevel::USER_RESTRICTED_NON_ADMIN as i32)
238 {
239 policy.initial_token_level = sandbox::TokenLevel::USER_RESTRICTED_NON_ADMIN;
240 }
241 if (policy.integrity_level as i32) > (sandbox::IntegrityLevel::INTEGRITY_LEVEL_MEDIUM as i32) {
242 policy.integrity_level = sandbox::IntegrityLevel::INTEGRITY_LEVEL_MEDIUM;
243 }
244 }
245
246 /// Adjust a policy to allow perfetto tracing to open shared memory and use WASAPI.
247 #[cfg(feature = "sandbox")]
adjust_perfetto_policy(policy: &mut sandbox::policy::Policy)248 fn adjust_perfetto_policy(policy: &mut sandbox::policy::Policy) {
249 if (policy.initial_token_level as i32)
250 < (sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS as i32)
251 {
252 policy.initial_token_level = sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS;
253 }
254
255 if (policy.lockdown_token_level as i32)
256 < (sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS as i32)
257 {
258 policy.lockdown_token_level = sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS;
259 }
260
261 if (policy.integrity_level as i32) > (sandbox::IntegrityLevel::INTEGRITY_LEVEL_MEDIUM as i32) {
262 policy.integrity_level = sandbox::IntegrityLevel::INTEGRITY_LEVEL_MEDIUM;
263 }
264
265 if (policy.delayed_integrity_level as i32)
266 > (sandbox::IntegrityLevel::INTEGRITY_LEVEL_MEDIUM as i32)
267 {
268 policy.delayed_integrity_level = sandbox::IntegrityLevel::INTEGRITY_LEVEL_MEDIUM;
269 }
270 }
271
272 /// Wrapper that terminates a child process (if running) when dropped.
273 struct ChildCleanup {
274 process_type: ProcessType,
275 child: Box<dyn Child>,
276 dh_tube: Option<Tube>,
277 }
278
279 #[derive(Debug)]
280 struct UnsandboxedChild(process::Child);
281 #[derive(Debug)]
282 struct SandboxedChild(SafeDescriptor);
283
284 impl AsRawDescriptor for UnsandboxedChild {
as_raw_descriptor(&self) -> RawDescriptor285 fn as_raw_descriptor(&self) -> RawDescriptor {
286 self.0.as_raw_handle()
287 }
288 }
289
290 impl AsRawDescriptor for SandboxedChild {
as_raw_descriptor(&self) -> RawDescriptor291 fn as_raw_descriptor(&self) -> RawDescriptor {
292 self.0.as_raw_descriptor()
293 }
294 }
295
296 impl Display for ChildCleanup {
fmt(&self, f: &mut Formatter) -> fmt::Result297 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
298 write!(f, "{:?} {:?}", self.process_type, self.child)
299 }
300 }
301
302 trait Child: std::fmt::Debug + AsRawDescriptor {
wait(&mut self) -> std::io::Result<Option<ExitCode>>303 fn wait(&mut self) -> std::io::Result<Option<ExitCode>>;
try_wait(&mut self) -> std::io::Result<Option<ExitCode>>304 fn try_wait(&mut self) -> std::io::Result<Option<ExitCode>>;
kill(&mut self) -> std::io::Result<()>305 fn kill(&mut self) -> std::io::Result<()>;
306 // Necessary to upcast dyn Child to dyn AsRawDescriptor
as_descriptor(&self) -> &dyn AsRawDescriptor307 fn as_descriptor(&self) -> &dyn AsRawDescriptor;
308 }
309
310 impl Child for UnsandboxedChild {
wait(&mut self) -> std::io::Result<Option<ExitCode>>311 fn wait(&mut self) -> std::io::Result<Option<ExitCode>> {
312 Ok(self.0.wait()?.code())
313 }
314
try_wait(&mut self) -> std::io::Result<Option<ExitCode>>315 fn try_wait(&mut self) -> std::io::Result<Option<ExitCode>> {
316 if let Some(status) = self.0.try_wait()? {
317 Ok(status.code())
318 } else {
319 Ok(None)
320 }
321 }
322
kill(&mut self) -> std::io::Result<()>323 fn kill(&mut self) -> std::io::Result<()> {
324 self.0.kill()
325 }
326
as_descriptor(&self) -> &dyn AsRawDescriptor327 fn as_descriptor(&self) -> &dyn AsRawDescriptor {
328 self
329 }
330 }
331
332 impl Child for SandboxedChild {
wait(&mut self) -> std::io::Result<Option<ExitCode>>333 fn wait(&mut self) -> std::io::Result<Option<ExitCode>> {
334 let wait_ctx = WaitContext::<u32>::new()?;
335 wait_ctx.add(&self.0, 0)?;
336 let _events = wait_ctx.wait()?;
337 self.try_wait()
338 }
339
try_wait(&mut self) -> std::io::Result<Option<ExitCode>>340 fn try_wait(&mut self) -> std::io::Result<Option<ExitCode>> {
341 get_exit_code_process(self.0.as_raw_descriptor()).map(|code| code.map(|c| c as i32))
342 }
343
kill(&mut self) -> std::io::Result<()>344 fn kill(&mut self) -> std::io::Result<()> {
345 // TODO(b/315998194): Add safety comment
346 #[allow(clippy::undocumented_unsafe_blocks)]
347 if unsafe { TerminateProcess(self.0.as_raw_descriptor(), KILL_CHILD_EXIT_CODE) == 0 } {
348 Err(std::io::Error::last_os_error())
349 } else {
350 Ok(())
351 }
352 }
353
as_descriptor(&self) -> &dyn AsRawDescriptor354 fn as_descriptor(&self) -> &dyn AsRawDescriptor {
355 self
356 }
357 }
358
359 impl Drop for ChildCleanup {
drop(&mut self)360 fn drop(&mut self) {
361 let kill_process = match self.child.try_wait() {
362 Ok(None) => true,
363 Ok(_) => false,
364 Err(_) => true,
365 };
366 if kill_process {
367 if let Err(e) = self.child.kill() {
368 const ACCESS_DENIED: Option<i32> = Some(ERROR_ACCESS_DENIED as i32);
369 if !matches!(e.raw_os_error(), ACCESS_DENIED) {
370 error!("Failed to clean up child process {}: {}", self, e);
371 }
372 }
373
374 // Sending a kill signal does NOT imply the process has exited. Wait for it to exit.
375 let wait_res = self.child.wait();
376 if let Ok(Some(code)) = wait_res.as_ref() {
377 warn!(
378 "child process {} killed, exited {}",
379 self,
380 ExitCodeWrapper(*code)
381 );
382 } else {
383 error!(
384 "failed to wait for child process {} that was terminated: {:?}",
385 self, wait_res
386 );
387 }
388 } else {
389 info!("child process {} already terminated", self);
390 }
391
392 // Log child exit code regardless of whether we killed it or it exited
393 // on its own.
394 {
395 // Don't even attempt to log metrics process, it doesn't exist to log
396 // itself.
397 if self.process_type != ProcessType::Metrics {
398 let exit_code = self.child.wait();
399 if let Ok(Some(exit_code)) = exit_code {
400 metrics::log_event(MetricEventType::ChildProcessExit {
401 exit_code: exit_code as u32,
402 process_type: self.process_type,
403 });
404 } else {
405 error!(
406 "Failed to log exit code for process: {:?}, couldn't get exit code",
407 self.process_type
408 );
409 }
410 }
411 }
412 }
413 }
414
415 /// Represents a child process spawned by the broker.
416 struct ChildProcess {
417 // This is unused, but we hold it open to avoid an EPIPE in the child if it doesn't
418 // immediately read its startup information. We don't use FlushFileBuffers to avoid this
419 // because that would require blocking the startup sequence.
420 tube_transporter: TubeTransporter,
421
422 // Used to set up the child process. Unused in steady state.
423 bootstrap_tube: Tube,
424 // Child process PID.
425 process_id: u32,
426 alias_pid: u32,
427 }
428
429 /// Wrapper to start the broker.
run(cfg: Config, log_args: LogArgs) -> Result<()>430 pub fn run(cfg: Config, log_args: LogArgs) -> Result<()> {
431 // This wrapper exists because errors that are returned up to the caller aren't logged, though
432 // they are used to generate the return code. For practical debugging though, we want to log the
433 // errors.
434 let res = run_internal(cfg, log_args);
435 if let Err(e) = &res {
436 error!("Broker encountered an error: {}", e);
437 }
438 res
439 }
440
441 #[derive(EventToken)]
442 enum Token {
443 Sigterm,
444 Process(u32),
445 MainExitTimeout,
446 DeviceExitTimeout,
447 MetricsExitTimeout,
448 SigtermTimeout,
449 DuplicateHandle(u32),
450 }
451
get_log_path(cfg: &Config, file_name: &str) -> Option<PathBuf>452 fn get_log_path(cfg: &Config, file_name: &str) -> Option<PathBuf> {
453 cfg.logs_directory
454 .as_ref()
455 .map(|dir| Path::new(dir).join(file_name))
456 }
457
458 /// Creates a metrics tube pair for communication with the metrics process.
459 /// The returned Tube will be used by the process producing logs, while
460 /// the metric_tubes list is sent to the metrics process to receive logs.
461 ///
462 /// IMPORTANT NOTE: The metrics process must receive the client (second) end
463 /// of the Tube pair in order to allow the connection to be properly shut
464 /// down without data loss.
metrics_tube_pair(metric_tubes: &mut Vec<RecvTube>) -> Result<SendTube>465 fn metrics_tube_pair(metric_tubes: &mut Vec<RecvTube>) -> Result<SendTube> {
466 // TODO(nkgold): as written, this Tube pair won't handle ancillary data properly because the
467 // PIDs are not set properly at each end; however, we don't plan to send ancillary data.
468 let (t1, t2) =
469 Tube::directional_pair().exit_context(Exit::CreateTube, "failed to create tube")?;
470 metric_tubes.push(t2);
471 Ok(t1)
472 }
473
474 #[cfg(feature = "crash-report")]
create_crash_report_attrs(cfg: &Config, product_type: &str) -> CrashReportAttributes475 pub fn create_crash_report_attrs(cfg: &Config, product_type: &str) -> CrashReportAttributes {
476 crash_report::CrashReportAttributes {
477 product_type: product_type.to_owned(),
478 pipe_name: cfg.crash_pipe_name.clone(),
479 report_uuid: cfg.crash_report_uuid.clone(),
480 product_name: cfg.product_name.clone(),
481 product_version: cfg.product_version.clone(),
482 }
483 }
484
485 /// Setup crash reporting for a process. Each process MUST provide a unique `product_type` to avoid
486 /// making crash reports incomprehensible.
487 #[cfg(feature = "crash-report")]
setup_emulator_crash_reporting(cfg: &Config) -> Result<String>488 pub fn setup_emulator_crash_reporting(cfg: &Config) -> Result<String> {
489 crash_report::setup_crash_reporting(create_crash_report_attrs(
490 cfg,
491 crash_report::product_type::EMULATOR,
492 ))
493 .exit_context(
494 Exit::CrashReportingInit,
495 "failed to initialize crash reporting",
496 )
497 }
498
499 /// Starts the broker, which in turn spawns the main process & vhost user devices.
500 /// General data flow for device & main process spawning:
501 /// Each platform (e.g. linux.rs) will provide create_inputs/gpus/nets.
502 ///
503 /// Those functions will return a list of pairs of structs (containing the pipes and other
504 /// process specific configuration) for the VMM & backend sides of the device. These structs
505 /// should be minimal, and not duplicate information that is otherwise available in the Config
506 /// struct. There MAY be two different types per device, one for the VMM side, and another for
507 /// the backend.
508 ///
509 /// The broker will send all the VMM structs to the main process, and the other structs
510 /// to the vhost user backends. Every process will get a copy of the Config struct.
511 ///
512 /// Finally, the broker will wait on the child processes to exit, and handle errors.
513 ///
514 /// Refrain from using platform specific code within this function. It will eventually be cross
515 /// platform.
run_internal(mut cfg: Config, log_args: LogArgs) -> Result<()>516 fn run_internal(mut cfg: Config, log_args: LogArgs) -> Result<()> {
517 #[cfg(feature = "sandbox")]
518 if sandbox::is_sandbox_broker() {
519 // Get the BrokerServices pointer so that it gets initialized.
520 sandbox::BrokerServices::get()
521 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
522 }
523 // Note that parsing args causes syslog's log file to be set to the log file for the "main"
524 // process. We don't want broker logs going there, so we fetch our own log file and set it here.
525 let mut log_cfg = LogConfig {
526 log_args: log_args.clone(),
527 ..Default::default()
528 };
529
530 if let Some(log_path) = get_log_path(&cfg, "broker_syslog.log") {
531 log_cfg.pipe = Some(Box::new(
532 OpenOptions::new()
533 .append(true)
534 .create(true)
535 .open(log_path.as_path())
536 .with_exit_context(Exit::LogFile, || {
537 format!("failed to open log file {}", log_path.display())
538 })?,
539 ));
540 log_cfg.log_args.stderr = false;
541 } else {
542 log_cfg.log_args.stderr = true;
543 }
544 syslog::init_with(log_cfg)?;
545
546 #[cfg(feature = "process-invariants")]
547 let process_invariants = init_broker_process_invariants(
548 &cfg.process_invariants_data_handle,
549 &cfg.process_invariants_data_size,
550 )
551 .exit_context(
552 Exit::ProcessInvariantsInit,
553 "failed to initialize process invariants",
554 )?;
555
556 #[cfg(feature = "crash-report")]
557 init_broker_crash_reporting(&mut cfg)?;
558
559 let _raise_timer_resolution = enable_high_res_timers()
560 .exit_context(Exit::EnableHighResTimer, "failed to enable high res timers")?;
561
562 // Note: in case of an error / scope exit, any children still in this map will be automatically
563 // closed.
564 let mut children: HashMap<u32, ChildCleanup> = HashMap::new();
565
566 let mut exit_events = Vec::new();
567 let mut wait_ctx: WaitContext<Token> = WaitContext::new()
568 .exit_context(Exit::CreateWaitContext, "failed to create event context")?;
569
570 // Hook ^C / SIGTERM so we can handle it gracefully.
571 let sigterm_event = Event::new().exit_context(Exit::CreateEvent, "failed to create event")?;
572 let sigterm_event_ctrlc = sigterm_event
573 .try_clone()
574 .exit_context(Exit::CloneEvent, "failed to clone event")?;
575 ctrlc::set_handler(move || {
576 sigterm_event_ctrlc.signal().unwrap();
577 })
578 .exit_context(Exit::SetSigintHandler, "failed to set sigint handler")?;
579 wait_ctx.add(&sigterm_event, Token::Sigterm).exit_context(
580 Exit::WaitContextAdd,
581 "failed to add trigger to event context",
582 )?;
583
584 let mut metric_tubes = Vec::new();
585 let metrics_controller = spawn_child(
586 current_exe().unwrap().to_str().unwrap(),
587 ["run-metrics"],
588 get_log_path(&cfg, "metrics_stdout.log"),
589 get_log_path(&cfg, "metrics_stderr.log"),
590 ProcessType::Metrics,
591 &mut children,
592 &mut wait_ctx,
593 /* skip_bootstrap= */
594 #[cfg(test)]
595 false,
596 /* use_sandbox= */
597 cfg.jail_config.is_some(),
598 Vec::new(),
599 &cfg,
600 )?;
601 metrics_controller
602 .tube_transporter
603 .serialize_and_transport(metrics_controller.process_id)
604 .exit_context(Exit::TubeTransporterInit, "failed to initialize tube")?;
605
606 let mut main_child = spawn_child(
607 current_exe().unwrap().to_str().unwrap(),
608 ["run-main"],
609 get_log_path(&cfg, "main_stdout.log"),
610 get_log_path(&cfg, "main_stderr.log"),
611 ProcessType::Main,
612 &mut children,
613 &mut wait_ctx,
614 /* skip_bootstrap= */
615 #[cfg(test)]
616 false,
617 /* use_sandbox= */
618 cfg.jail_config.is_some(),
619 Vec::new(),
620 &cfg,
621 )?;
622
623 // Save block children `ChildProcess` so TubeTransporter and Tubes don't get closed.
624 let _block_children = start_up_block_backends(
625 &mut cfg,
626 &log_args,
627 &mut children,
628 &mut exit_events,
629 &mut wait_ctx,
630 &mut main_child,
631 &mut metric_tubes,
632 #[cfg(feature = "process-invariants")]
633 &process_invariants,
634 )?;
635
636 #[cfg(all(feature = "net", feature = "slirp"))]
637 let (_slirp_child, _net_children) = start_up_net_backend(
638 &mut main_child,
639 &mut children,
640 &mut exit_events,
641 &mut wait_ctx,
642 &mut cfg,
643 &log_args,
644 &mut metric_tubes,
645 #[cfg(feature = "process-invariants")]
646 &process_invariants,
647 )?;
648
649 #[cfg(feature = "audio")]
650 let snd_cfg = platform_create_snd(&cfg, &mut main_child, &mut exit_events)?;
651
652 #[cfg(feature = "audio")]
653 let _snd_child = if !cfg
654 .vhost_user
655 .iter()
656 .any(|opt| opt.type_ == DeviceType::Sound)
657 {
658 // Pass both backend and frontend configs to main process.
659 cfg.snd_split_config = Some(snd_cfg);
660 None
661 } else {
662 Some(start_up_snd(
663 &mut cfg,
664 &log_args,
665 snd_cfg,
666 &mut main_child,
667 &mut children,
668 &mut wait_ctx,
669 &mut metric_tubes,
670 #[cfg(feature = "process-invariants")]
671 &process_invariants,
672 )?)
673 };
674
675 let (vm_evt_wrtube, vm_evt_rdtube) =
676 Tube::directional_pair().context("failed to create vm event tube")?;
677
678 #[cfg(feature = "gpu")]
679 let mut input_event_split_config = platform_create_input_event_config(&cfg)
680 .context("create input event devices for virtio-gpu device")?;
681
682 #[cfg(feature = "gpu")]
683 let mut window_procedure_thread_builder = Some(WindowProcedureThread::builder());
684
685 #[cfg(feature = "gpu")]
686 let gpu_cfg = platform_create_gpu(
687 &cfg,
688 &mut main_child,
689 &mut exit_events,
690 vm_evt_wrtube
691 .try_clone()
692 .exit_context(Exit::CloneEvent, "failed to clone event")?,
693 )?;
694
695 #[cfg(feature = "gpu")]
696 let _gpu_child = if let Some(mut gpu_cfg) = gpu_cfg {
697 if !cfg
698 .vhost_user
699 .iter()
700 .any(|opt| opt.type_ == DeviceType::Gpu)
701 {
702 // Pass both backend and frontend configs to main process.
703 cfg.gpu_backend_config = Some(gpu_cfg.0);
704 cfg.gpu_vmm_config = Some(gpu_cfg.1);
705 None
706 } else {
707 // If we are running in a separate process, turn on external blobs (memory will be
708 // exported, sent to VMM for import, then mapped).
709 gpu_cfg.0.params.external_blob = true;
710
711 Some(start_up_gpu(
712 &mut cfg,
713 &log_args,
714 gpu_cfg,
715 &mut input_event_split_config,
716 &mut main_child,
717 &mut children,
718 &mut wait_ctx,
719 &mut metric_tubes,
720 window_procedure_thread_builder
721 .take()
722 .ok_or_else(|| anyhow!("window_procedure_thread_builder is missing."))?,
723 #[cfg(feature = "process-invariants")]
724 &process_invariants,
725 )?)
726 }
727 } else {
728 None
729 };
730
731 #[cfg(feature = "gpu")]
732 {
733 cfg.input_event_split_config = Some(input_event_split_config);
734 if let Some(window_procedure_thread_builder) = window_procedure_thread_builder {
735 cfg.window_procedure_thread_split_config = Some(
736 platform_create_window_procedure_thread_configs(
737 &cfg,
738 window_procedure_thread_builder,
739 main_child.alias_pid,
740 main_child.alias_pid,
741 )
742 .context("Failed to create window procedure thread configs")?,
743 );
744 }
745 }
746
747 // Wait until all device processes are spun up so main TubeTransporter will have all the
748 // device control and Vhost tubes.
749 main_child
750 .tube_transporter
751 .serialize_and_transport(main_child.process_id)
752 .exit_context(Exit::TubeTransporterInit, "failed to initialize tube")?;
753 main_child.bootstrap_tube.send(&cfg).unwrap();
754
755 let main_startup_args = CommonChildStartupArgs::new(
756 &log_args,
757 get_log_path(&cfg, "main_syslog.log"),
758 #[cfg(feature = "crash-report")]
759 create_crash_report_attrs(&cfg, product_type::EMULATOR),
760 #[cfg(feature = "process-invariants")]
761 process_invariants.clone(),
762 Some(metrics_tube_pair(&mut metric_tubes)?),
763 )?;
764 main_child.bootstrap_tube.send(&main_startup_args).unwrap();
765
766 let exit_event = Event::new().exit_context(Exit::CreateEvent, "failed to create event")?;
767 main_child.bootstrap_tube.send(&exit_event).unwrap();
768 exit_events.push(exit_event);
769
770 let broker_tubes = BrokerTubes {
771 vm_evt_wrtube,
772 vm_evt_rdtube,
773 };
774 main_child.bootstrap_tube.send(&broker_tubes).unwrap();
775
776 // Setup our own metrics agent
777 {
778 let broker_metrics = metrics_tube_pair(&mut metric_tubes)?;
779 metrics::initialize(broker_metrics);
780
781 #[cfg(feature = "gpu")]
782 let use_vulkan = match &cfg.gpu_parameters {
783 Some(params) => Some(params.use_vulkan),
784 None => {
785 warn!("No GPU parameters set on crosvm config.");
786 None
787 }
788 };
789 #[cfg(not(feature = "gpu"))]
790 let use_vulkan = None;
791
792 anti_tamper::setup_common_metric_invariants(
793 &cfg.product_version,
794 &cfg.product_channel,
795 &use_vulkan.unwrap_or_default(),
796 );
797 }
798
799 // We have all the metrics tubes from other children, so give them to the metrics controller
800 // along with a startup configuration.
801 let metrics_startup_args = CommonChildStartupArgs::new(
802 &log_args,
803 get_log_path(&cfg, "metrics_syslog.log"),
804 #[cfg(feature = "crash-report")]
805 create_crash_report_attrs(&cfg, product_type::METRICS),
806 #[cfg(feature = "process-invariants")]
807 process_invariants.clone(),
808 None,
809 )?;
810 metrics_controller
811 .bootstrap_tube
812 .send(&metrics_startup_args)
813 .unwrap();
814
815 metrics_controller
816 .bootstrap_tube
817 .send(&metric_tubes)
818 .unwrap();
819
820 Supervisor::broker_supervise_loop(children, wait_ctx, exit_events)
821 }
822
823 /// Shuts down the metrics process, waiting for it to close to ensure
824 /// all logs are flushed.
clean_up_metrics(metrics_child: ChildCleanup) -> Result<()>825 fn clean_up_metrics(metrics_child: ChildCleanup) -> Result<()> {
826 // This will close the final metrics connection, triggering a metrics
827 // process shutdown.
828 metrics::get_destructor().cleanup();
829
830 // However, we still want to wait for the metrics process to finish
831 // flushing any pending logs before exiting.
832 let metrics_cleanup_wait = WaitContext::<u32>::new().exit_context(
833 Exit::CreateWaitContext,
834 "failed to create metrics wait context",
835 )?;
836 let mut metrics_timeout =
837 Timer::new().exit_context(Exit::CreateTimer, "failed to create metrics timeout timer")?;
838 metrics_timeout
839 .reset(EXIT_TIMEOUT, None)
840 .exit_context(Exit::ResetTimer, "failed to reset timer")?;
841 metrics_cleanup_wait.add(&metrics_timeout, 0).exit_context(
842 Exit::WaitContextAdd,
843 "failed to add metrics timout to wait context",
844 )?;
845 metrics_cleanup_wait
846 .add(metrics_child.child.as_descriptor(), 1)
847 .exit_context(
848 Exit::WaitContextAdd,
849 "failed to add metrics process to wait context",
850 )?;
851 let events = metrics_cleanup_wait
852 .wait()
853 .context("failed to wait for metrics context")?;
854
855 let mut process_exited = false;
856 if events.iter().any(|e| e.is_readable && e.token == 1) {
857 process_exited = true;
858 }
859
860 if !process_exited {
861 warn!(
862 "broker: Metrics process timed out before cleanly exiting.
863 This may indicate some logs remain unsent."
864 );
865 // Process will be force-killed on drop
866 }
867
868 Ok(())
869 }
870
871 #[cfg(feature = "crash-report")]
init_broker_crash_reporting(cfg: &mut Config) -> Result<()>872 fn init_broker_crash_reporting(cfg: &mut Config) -> Result<()> {
873 cfg.crash_report_uuid = Some(generate_uuid());
874 if cfg.crash_pipe_name.is_none() {
875 // We weren't started by the service. Spin up a crash reporter to be shared with all
876 // children.
877 cfg.crash_pipe_name = Some(
878 crash_report::setup_crash_reporting(create_crash_report_attrs(
879 cfg,
880 product_type::BROKER,
881 ))
882 .exit_context(Exit::CrashReportingInit, "failed to init crash reporting")?,
883 );
884 } else {
885 crash_report::setup_crash_reporting(create_crash_report_attrs(cfg, product_type::BROKER))
886 .exit_context(Exit::CrashReportingInit, "failed to init crash reporting")?;
887 }
888
889 Ok(())
890 }
891
892 struct Supervisor {
893 children: HashMap<u32, ChildCleanup>,
894 wait_ctx: WaitContext<Token>,
895 exit_events: Vec<Event>,
896 exit_timer: Option<Timer>,
897 }
898
899 impl Supervisor {
broker_supervise_loop( children: HashMap<u32, ChildCleanup>, wait_ctx: WaitContext<Token>, exit_events: Vec<Event>, ) -> Result<()>900 pub fn broker_supervise_loop(
901 children: HashMap<u32, ChildCleanup>,
902 wait_ctx: WaitContext<Token>,
903 exit_events: Vec<Event>,
904 ) -> Result<()> {
905 let mut supervisor = Supervisor {
906 children,
907 wait_ctx,
908 exit_events,
909 exit_timer: None,
910 };
911 let result = supervisor.broker_loop();
912
913 // Once supervise loop exits, we are exiting and just need to clean
914 // up. In error cases, there could still be children processes, so we close
915 // those first, and finally drop the metrics process.
916 supervisor.children.retain(|_, child| {
917 match child.process_type {
918 ProcessType::Metrics => true,
919 _ => {
920 warn!(
921 "broker: Forcibly closing child (type: {:?}). This often means
922 the child was unable to close within the normal timeout window,
923 or the broker itself failed with an error.",
924 child.process_type
925 );
926 // Child killed on drop
927 false
928 }
929 }
930 });
931
932 {
933 if supervisor.is_only_metrics_process_running() {
934 clean_up_metrics(supervisor.children.into_values().next().unwrap())?;
935 } else {
936 warn!(
937 "broker: Metrics process not running after cleanup.
938 This may indicate some exit logs have been dropped."
939 );
940 }
941 }
942
943 result
944 }
945
946 /// We require exactly one main process.
assert_children_sane(&mut self)947 fn assert_children_sane(&mut self) {
948 let main_processes = self
949 .children
950 .iter()
951 .filter(|(_, child)| child.process_type == ProcessType::Main)
952 .count();
953 if main_processes != 1 {
954 // Why do we have to clear children? Well, panic *can* cause destructors not to run,
955 // which means these children won't run. The exact explanation for this isn't clear, but
956 // it reproduced consistently. So since we're panicking, we'll be careful.
957 self.children.clear();
958 panic!(
959 "Broker must supervise exactly one main process. Got {} main process(es).",
960 main_processes,
961 )
962 }
963 }
964
is_only_metrics_process_running(&self) -> bool965 fn is_only_metrics_process_running(&self) -> bool {
966 self.children.len() == 1
967 && self.children.values().next().unwrap().process_type == ProcessType::Metrics
968 }
969
all_non_metrics_processes_exited(&self) -> bool970 fn all_non_metrics_processes_exited(&self) -> bool {
971 self.children.is_empty() || self.is_only_metrics_process_running()
972 }
973
start_exit_timer(&mut self, timeout_token: Token) -> Result<()>974 fn start_exit_timer(&mut self, timeout_token: Token) -> Result<()> {
975 if self.exit_timer.is_some() {
976 return Ok(());
977 }
978
979 let mut et = Timer::new().exit_context(Exit::CreateTimer, "failed to create timer")?;
980 et.reset(EXIT_TIMEOUT, None)
981 .exit_context(Exit::ResetTimer, "failed to reset timer")?;
982 self.wait_ctx.add(&et, timeout_token).exit_context(
983 Exit::WaitContextAdd,
984 "failed to add trigger to wait context",
985 )?;
986 self.exit_timer = Some(et);
987
988 Ok(())
989 }
990
991 /// Once children have been spawned, this function is called to run the supervision loop, which
992 /// waits for processes to exit and handles errors.
broker_loop(&mut self) -> Result<()>993 fn broker_loop(&mut self) -> Result<()> {
994 const KILLED_BY_SIGNAL: ExitCode = Exit::KilledBySignal as ExitCode;
995 self.assert_children_sane();
996 let mut first_nonzero_exitcode = None;
997
998 while !self.all_non_metrics_processes_exited() {
999 let events = self
1000 .wait_ctx
1001 .wait()
1002 .context("failed to wait for event context")?;
1003
1004 for event in events.iter().filter(|e| e.is_readable) {
1005 match event.token {
1006 Token::Sigterm => {
1007 // Signal all children other than metrics to exit.
1008 for exit_event in &self.exit_events {
1009 if let Err(e) = exit_event.signal() {
1010 error!("failed to signal exit event to child: {}", e);
1011 }
1012 }
1013 first_nonzero_exitcode.get_or_insert(KILLED_BY_SIGNAL);
1014 self.start_exit_timer(Token::SigtermTimeout)?;
1015 }
1016 Token::Process(child_id) => {
1017 let mut child = self.children.remove(&child_id).unwrap();
1018 let process_handle = Descriptor(child.child.as_raw_descriptor());
1019 self.wait_ctx.delete(&process_handle).exit_context(
1020 Exit::WaitContextDelete,
1021 "failed to remove trigger from event context",
1022 )?;
1023 if let Some(dh_tube) = child.dh_tube.as_ref() {
1024 self.wait_ctx
1025 .delete(dh_tube.get_read_notifier())
1026 .exit_context(
1027 Exit::WaitContextDelete,
1028 "failed to remove trigger from event context",
1029 )?;
1030 }
1031
1032 let exit_code = child.child.wait().unwrap().unwrap();
1033 info!(
1034 "broker: child (type {:?}) exited {}",
1035 child.process_type,
1036 ExitCodeWrapper(exit_code),
1037 );
1038
1039 // Save the child's exit code (to pass through to the broker's exit code) if
1040 // none has been saved or if the previously saved exit code was
1041 // KilledBySignal. We overwrite KilledBySignal because the child exit may
1042 // race with the sigterm from the service, esp if child exit is slowed by a
1043 // Crashpad dump, and we don't want to lose the child's exit code if it was
1044 // the initial cause of the emulator failing.
1045 if exit_code != 0
1046 && (first_nonzero_exitcode.is_none()
1047 || matches!(first_nonzero_exitcode, Some(KILLED_BY_SIGNAL)))
1048 {
1049 info!(
1050 "setting first_nonzero_exitcode {:?} -> {}",
1051 first_nonzero_exitcode, exit_code,
1052 );
1053 first_nonzero_exitcode =
1054 Some(to_process_type_error(exit_code as u32, child.process_type)
1055 as i32);
1056 }
1057
1058 let timeout_token = match child.process_type {
1059 ProcessType::Main => Token::MainExitTimeout,
1060 ProcessType::Metrics => Token::MetricsExitTimeout,
1061 _ => Token::DeviceExitTimeout,
1062 };
1063 self.start_exit_timer(timeout_token)?;
1064 }
1065 Token::SigtermTimeout => {
1066 if let Some(exit_code) = first_nonzero_exitcode {
1067 if exit_code != KILLED_BY_SIGNAL {
1068 bail_exit_code!(
1069 exit_code,
1070 "broker got sigterm, but a child exited with an error.",
1071 );
1072 }
1073 }
1074 ensure_exit_code!(
1075 self.all_non_metrics_processes_exited(),
1076 Exit::BrokerSigtermTimeout,
1077 "broker got sigterm, but other broker children did not exit within the \
1078 timeout",
1079 );
1080 }
1081 Token::MainExitTimeout => {
1082 if let Some(exit_code) = first_nonzero_exitcode {
1083 bail_exit_code!(
1084 exit_code,
1085 "main exited, but a child exited with an error.",
1086 );
1087 }
1088 ensure_exit_code!(
1089 self.all_non_metrics_processes_exited(),
1090 Exit::BrokerMainExitedTimeout,
1091 "main exited, but other broker children did not exit within the \
1092 timeout",
1093 );
1094 }
1095 Token::DeviceExitTimeout => {
1096 // A device process exited, but there are still other processes running.
1097 if let Some(exit_code) = first_nonzero_exitcode {
1098 bail_exit_code!(
1099 exit_code,
1100 "a device exited, and either it or another child exited with an \
1101 error.",
1102 );
1103 }
1104 ensure_exit_code!(
1105 self.all_non_metrics_processes_exited(),
1106 Exit::BrokerDeviceExitedTimeout,
1107 "device exited, but other broker children did not exit within the \
1108 timeout",
1109 );
1110 }
1111 Token::MetricsExitTimeout => {
1112 // The metrics server exited, but there are still other processes running.
1113 if let Some(exit_code) = first_nonzero_exitcode {
1114 bail_exit_code!(
1115 exit_code,
1116 "metrics server exited, and either it or another child exited with \
1117 an error.",
1118 );
1119 }
1120 ensure_exit_code!(
1121 self.children.is_empty(),
1122 Exit::BrokerMetricsExitedTimeout,
1123 "metrics exited, but other broker children did not exit within the \
1124 timeout",
1125 );
1126 }
1127 Token::DuplicateHandle(child_id) => {
1128 if let Some(tube) = &self.children[&child_id].dh_tube {
1129 let req: DuplicateHandleRequest = tube
1130 .recv()
1131 .exit_context(Exit::TubeFailure, "failed operation on tube")?;
1132 if !self.children.contains_key(&req.target_alias_pid) {
1133 error!(
1134 "DuplicateHandleRequest contained invalid alias pid: {}",
1135 req.target_alias_pid
1136 );
1137 tube.send(&DuplicateHandleResponse { handle: None })
1138 .exit_context(Exit::TubeFailure, "failed operation on tube")?;
1139 } else {
1140 let target = &self.children[&req.target_alias_pid].child;
1141 let handle = win_util::duplicate_handle_from_source_process(
1142 self.children[&child_id].child.as_raw_descriptor(),
1143 req.handle as RawHandle,
1144 target.as_raw_descriptor(),
1145 );
1146 match handle {
1147 Ok(handle) => tube
1148 .send(&DuplicateHandleResponse {
1149 handle: Some(handle as usize),
1150 })
1151 .exit_context(
1152 Exit::TubeFailure,
1153 "failed operation on tube",
1154 )?,
1155 Err(e) => {
1156 error!("Failed to duplicate handle: {}", e);
1157 tube.send(&DuplicateHandleResponse { handle: None })
1158 .exit_context(
1159 Exit::TubeFailure,
1160 "failed operation on tube",
1161 )?
1162 }
1163 };
1164 }
1165 }
1166 }
1167 }
1168 }
1169 }
1170
1171 if let Some(exit_code) = first_nonzero_exitcode {
1172 bail_exit_code!(
1173 exit_code,
1174 if exit_code == KILLED_BY_SIGNAL {
1175 "broker got sigterm, and all children exited zero from shutdown event."
1176 } else {
1177 "all processes exited, but at least one encountered an error."
1178 },
1179 );
1180 }
1181
1182 Ok(())
1183 }
1184 }
1185
start_up_block_backends( cfg: &mut Config, log_args: &LogArgs, children: &mut HashMap<u32, ChildCleanup>, exit_events: &mut Vec<Event>, wait_ctx: &mut WaitContext<Token>, main_child: &mut ChildProcess, metric_tubes: &mut Vec<RecvTube>, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<Vec<ChildProcess>>1186 fn start_up_block_backends(
1187 cfg: &mut Config,
1188 log_args: &LogArgs,
1189 children: &mut HashMap<u32, ChildCleanup>,
1190 exit_events: &mut Vec<Event>,
1191 wait_ctx: &mut WaitContext<Token>,
1192 main_child: &mut ChildProcess,
1193 metric_tubes: &mut Vec<RecvTube>,
1194 #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
1195 ) -> Result<Vec<ChildProcess>> {
1196 let mut block_children = Vec::new();
1197 let disk_options = cfg.disks.clone();
1198 for (index, disk_option) in disk_options.iter().enumerate() {
1199 let block_child = spawn_block_backend(index, main_child, children, wait_ctx, cfg)?;
1200
1201 let startup_args = CommonChildStartupArgs::new(
1202 log_args,
1203 get_log_path(cfg, &format!("disk_{}_syslog.log", index)),
1204 #[cfg(feature = "crash-report")]
1205 create_crash_report_attrs(cfg, &format!("{}_{}", product_type::DISK, index)),
1206 #[cfg(feature = "process-invariants")]
1207 process_invariants.clone(),
1208 Some(metrics_tube_pair(metric_tubes)?),
1209 )?;
1210 block_child.bootstrap_tube.send(&startup_args).unwrap();
1211
1212 block_child.bootstrap_tube.send(&disk_option).unwrap();
1213
1214 let exit_event = Event::new().exit_context(Exit::CreateEvent, "failed to create event")?;
1215 block_child.bootstrap_tube.send(&exit_event).unwrap();
1216 exit_events.push(exit_event);
1217 block_children.push(block_child);
1218 }
1219
1220 Ok(block_children)
1221 }
1222
spawn_block_backend( log_index: usize, main_child: &mut ChildProcess, children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, cfg: &mut Config, ) -> Result<ChildProcess>1223 fn spawn_block_backend(
1224 log_index: usize,
1225 main_child: &mut ChildProcess,
1226 children: &mut HashMap<u32, ChildCleanup>,
1227 wait_ctx: &mut WaitContext<Token>,
1228 cfg: &mut Config,
1229 ) -> Result<ChildProcess> {
1230 let (mut vhost_user_main_tube, mut vhost_user_device_tube) =
1231 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
1232
1233 let (mut disk_host_tube, mut disk_device_tube) =
1234 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
1235
1236 disk_device_tube.set_target_pid(main_child.alias_pid);
1237 vhost_user_device_tube.set_target_pid(main_child.alias_pid);
1238 let block_child = spawn_child(
1239 current_exe().unwrap().to_str().unwrap(),
1240 ["device", "block"],
1241 get_log_path(cfg, &format!("disk_{}_stdout.log", log_index)),
1242 get_log_path(cfg, &format!("disk_{}_stderr.log", log_index)),
1243 ProcessType::Block,
1244 children,
1245 wait_ctx,
1246 /* skip_bootstrap= */
1247 #[cfg(test)]
1248 false,
1249 /* use_sandbox= */
1250 cfg.jail_config.is_some(),
1251 vec![
1252 TubeTransferData {
1253 tube: disk_device_tube,
1254 tube_token: TubeToken::Control,
1255 },
1256 TubeTransferData {
1257 tube: vhost_user_device_tube,
1258 tube_token: TubeToken::VhostUser,
1259 },
1260 ],
1261 cfg,
1262 )?;
1263
1264 block_child
1265 .tube_transporter
1266 .serialize_and_transport(block_child.process_id)
1267 .exit_context(Exit::TubeTransporterInit, "failed to initialize tube")?;
1268
1269 vhost_user_main_tube.set_target_pid(block_child.alias_pid);
1270 disk_host_tube.set_target_pid(block_child.alias_pid);
1271 cfg.block_control_tube.push(disk_host_tube);
1272 cfg.block_vhost_user_tube.push(vhost_user_main_tube);
1273
1274 Ok(block_child)
1275 }
1276
1277 #[cfg(feature = "sandbox")]
spawn_sandboxed_child<I, S>( program: &str, args: I, stdout_file: Option<std::fs::File>, stderr_file: Option<std::fs::File>, handles_to_inherit: Vec<&dyn AsRawDescriptor>, process_policy: sandbox::policy::Policy, ) -> Result<(u32, Box<dyn Child>)> where I: IntoIterator<Item = S>, S: AsRef<OsStr>,1278 fn spawn_sandboxed_child<I, S>(
1279 program: &str,
1280 args: I,
1281 stdout_file: Option<std::fs::File>,
1282 stderr_file: Option<std::fs::File>,
1283 handles_to_inherit: Vec<&dyn AsRawDescriptor>,
1284 process_policy: sandbox::policy::Policy,
1285 ) -> Result<(u32, Box<dyn Child>)>
1286 where
1287 I: IntoIterator<Item = S>,
1288 S: AsRef<OsStr>,
1289 {
1290 let mut broker = sandbox::BrokerServices::get()
1291 .exit_context(Exit::SandboxError, "sandbox operation failed")?
1292 .unwrap();
1293 let mut policy = broker.create_policy();
1294 policy
1295 .set_token_level(
1296 process_policy.initial_token_level,
1297 process_policy.lockdown_token_level,
1298 )
1299 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1300 policy
1301 .set_job_level(process_policy.job_level, process_policy.ui_exceptions)
1302 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1303 policy
1304 .set_integrity_level(process_policy.integrity_level)
1305 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1306 policy
1307 .set_delayed_integrity_level(process_policy.delayed_integrity_level)
1308 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1309
1310 if process_policy.alternate_desktop {
1311 policy
1312 .set_alternate_desktop(process_policy.alternate_winstation)
1313 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1314 }
1315
1316 for rule in process_policy.exceptions {
1317 policy
1318 .add_rule(rule.subsystem, rule.semantics, rule.pattern)
1319 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1320 }
1321
1322 policy.set_lockdown_default_dacl();
1323
1324 if let Some(file) = stdout_file.as_ref() {
1325 policy
1326 .set_stdout_from_file(file)
1327 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1328 }
1329
1330 if let Some(file) = stderr_file.as_ref() {
1331 policy
1332 .set_stderr_from_file(file)
1333 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1334 }
1335
1336 for handle in handles_to_inherit.into_iter() {
1337 policy.add_handle_to_share(handle);
1338 }
1339
1340 for dll in process_policy.dll_blocklist.into_iter() {
1341 policy
1342 .add_dll_to_unload(&dll)
1343 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1344 }
1345
1346 // spawn_target uses CreateProcessW to create a new process, which will pass
1347 // the command line arguments verbatim to the new process. Most processes
1348 // expect that argv[0] will be the program name, so provide that before the
1349 // rest of the args.
1350 let command_line = args
1351 .into_iter()
1352 .fold(format!("\"{}\"", program), |mut args, arg| {
1353 args.push(' ');
1354 args.push_str(OsStr::new(&arg).to_str().unwrap());
1355 args
1356 });
1357
1358 let (target, warning) = broker
1359 .spawn_target(program, &command_line, &policy)
1360 .exit_context(Exit::SandboxError, "sandbox operation failed")?;
1361 if let Some(w) = warning {
1362 warn!("sandbox: got warning spawning target: {}", w);
1363 }
1364 win_util::resume_thread(target.thread.as_raw_descriptor())
1365 .exit_context(Exit::ProcessSpawnFailed, "failed to spawn child process")?;
1366
1367 Ok((target.process_id, Box::new(SandboxedChild(target.process))))
1368 }
1369
spawn_unsandboxed_child<I, S>( program: &str, args: I, stdout_file: Option<std::fs::File>, stderr_file: Option<std::fs::File>, handles_to_inherit: Vec<&dyn AsRawDescriptor>, ) -> Result<(u32, Box<dyn Child>)> where I: IntoIterator<Item = S>, S: AsRef<OsStr>,1370 fn spawn_unsandboxed_child<I, S>(
1371 program: &str,
1372 args: I,
1373 stdout_file: Option<std::fs::File>,
1374 stderr_file: Option<std::fs::File>,
1375 handles_to_inherit: Vec<&dyn AsRawDescriptor>,
1376 ) -> Result<(u32, Box<dyn Child>)>
1377 where
1378 I: IntoIterator<Item = S>,
1379 S: AsRef<OsStr>,
1380 {
1381 let mut proc = Command::new(program);
1382
1383 let proc = proc.args(args);
1384
1385 for handle in handles_to_inherit.iter() {
1386 win_util::set_handle_inheritance(handle.as_raw_descriptor(), /* inheritable= */ true)
1387 .exit_context(Exit::CreateSocket, "failed to create socket")?;
1388 }
1389
1390 if let Some(file) = stdout_file {
1391 proc.stdout(file);
1392 }
1393
1394 if let Some(file) = stderr_file {
1395 proc.stderr(file);
1396 }
1397
1398 info!("spawning process: {:?}", proc);
1399 let proc = proc
1400 .spawn()
1401 .exit_context(Exit::ProcessSpawnFailed, "failed to spawn child process")?;
1402
1403 for handle in handles_to_inherit.iter() {
1404 win_util::set_handle_inheritance(handle.as_raw_descriptor(), /* inheritable= */ false)
1405 .exit_context(Exit::CreateSocket, "failed to create socket")?;
1406 }
1407
1408 let process_id = proc.id();
1409
1410 Ok((process_id, Box::new(UnsandboxedChild(proc))))
1411 }
1412
1413 #[cfg(all(feature = "net", feature = "slirp"))]
start_up_net_backend( main_child: &mut ChildProcess, children: &mut HashMap<u32, ChildCleanup>, exit_events: &mut Vec<Event>, wait_ctx: &mut WaitContext<Token>, cfg: &mut Config, log_args: &LogArgs, metric_tubes: &mut Vec<RecvTube>, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<(ChildProcess, ChildProcess)>1414 fn start_up_net_backend(
1415 main_child: &mut ChildProcess,
1416 children: &mut HashMap<u32, ChildCleanup>,
1417 exit_events: &mut Vec<Event>,
1418 wait_ctx: &mut WaitContext<Token>,
1419 cfg: &mut Config,
1420 log_args: &LogArgs,
1421 metric_tubes: &mut Vec<RecvTube>,
1422 #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
1423 ) -> Result<(ChildProcess, ChildProcess)> {
1424 let (host_pipe, guest_pipe) = named_pipes::pair_with_buffer_size(
1425 &FramingMode::Message.into(),
1426 &BlockingMode::Blocking.into(),
1427 /* timeout= */ 0,
1428 /* buffer_size= */ SLIRP_BUFFER_SIZE,
1429 /* overlapped= */ true,
1430 )
1431 .expect("Failed to create named pipe pair.");
1432 let slirp_kill_event = Event::new().expect("Failed to create slirp kill event.");
1433
1434 let slirp_child = spawn_slirp(children, wait_ctx, cfg)?;
1435
1436 let slirp_child_startup_args = CommonChildStartupArgs::new(
1437 log_args,
1438 get_log_path(cfg, "slirp_syslog.log"),
1439 #[cfg(feature = "crash-report")]
1440 create_crash_report_attrs(cfg, product_type::SLIRP),
1441 #[cfg(feature = "process-invariants")]
1442 process_invariants.clone(),
1443 Some(metrics_tube_pair(metric_tubes)?),
1444 )?;
1445 slirp_child
1446 .bootstrap_tube
1447 .send(&slirp_child_startup_args)
1448 .unwrap();
1449
1450 let slirp_config = SlirpStartupConfig {
1451 slirp_pipe: host_pipe,
1452 shutdown_event: slirp_kill_event
1453 .try_clone()
1454 .expect("Failed to clone slirp kill event."),
1455 #[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))]
1456 slirp_capture_file: cfg.slirp_capture_file.take(),
1457 };
1458 slirp_child.bootstrap_tube.send(&slirp_config).unwrap();
1459
1460 let net_child = spawn_net_backend(main_child, children, wait_ctx, cfg)?;
1461
1462 let net_child_startup_args = CommonChildStartupArgs::new(
1463 log_args,
1464 get_log_path(cfg, "net_syslog.log"),
1465 #[cfg(feature = "crash-report")]
1466 create_crash_report_attrs(cfg, product_type::SLIRP),
1467 #[cfg(feature = "process-invariants")]
1468 process_invariants.clone(),
1469 Some(metrics_tube_pair(metric_tubes)?),
1470 )?;
1471 net_child
1472 .bootstrap_tube
1473 .send(&net_child_startup_args)
1474 .unwrap();
1475
1476 let net_backend_config = NetBackendConfig {
1477 guest_pipe,
1478 slirp_kill_event,
1479 };
1480 net_child.bootstrap_tube.send(&net_backend_config).unwrap();
1481 let exit_event = Event::new().exit_context(Exit::CreateEvent, "failed to create event")?;
1482 net_child.bootstrap_tube.send(&exit_event).unwrap();
1483 exit_events.push(exit_event);
1484
1485 Ok((slirp_child, net_child))
1486 }
1487
spawn_slirp( children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, cfg: &mut Config, ) -> Result<ChildProcess>1488 fn spawn_slirp(
1489 children: &mut HashMap<u32, ChildCleanup>,
1490 wait_ctx: &mut WaitContext<Token>,
1491 cfg: &mut Config,
1492 ) -> Result<ChildProcess> {
1493 let slirp_child = spawn_child(
1494 current_exe().unwrap().to_str().unwrap(),
1495 ["run-slirp"],
1496 get_log_path(cfg, "slirp_stdout.log"),
1497 get_log_path(cfg, "slirp_stderr.log"),
1498 ProcessType::Slirp,
1499 children,
1500 wait_ctx,
1501 /* skip_bootstrap= */
1502 #[cfg(test)]
1503 false,
1504 /* use_sandbox= */ cfg.jail_config.is_some(),
1505 vec![],
1506 cfg,
1507 )?;
1508
1509 slirp_child
1510 .tube_transporter
1511 .serialize_and_transport(slirp_child.process_id)
1512 .exit_context(Exit::TubeTransporterInit, "failed to initialize tube")?;
1513
1514 Ok(slirp_child)
1515 }
1516
spawn_net_backend( main_child: &mut ChildProcess, children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, cfg: &mut Config, ) -> Result<ChildProcess>1517 fn spawn_net_backend(
1518 main_child: &mut ChildProcess,
1519 children: &mut HashMap<u32, ChildCleanup>,
1520 wait_ctx: &mut WaitContext<Token>,
1521 cfg: &mut Config,
1522 ) -> Result<ChildProcess> {
1523 let (mut vhost_user_main_tube, mut vhost_user_device_tube) =
1524 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
1525
1526 vhost_user_device_tube.set_target_pid(main_child.alias_pid);
1527
1528 let net_child = spawn_child(
1529 current_exe().unwrap().to_str().unwrap(),
1530 ["device", "net"],
1531 get_log_path(cfg, "net_stdout.log"),
1532 get_log_path(cfg, "net_stderr.log"),
1533 ProcessType::Net,
1534 children,
1535 wait_ctx,
1536 /* skip_bootstrap= */
1537 #[cfg(test)]
1538 false,
1539 /* use_sandbox= */ cfg.jail_config.is_some(),
1540 vec![TubeTransferData {
1541 tube: vhost_user_device_tube,
1542 tube_token: TubeToken::VhostUser,
1543 }],
1544 cfg,
1545 )?;
1546
1547 net_child
1548 .tube_transporter
1549 .serialize_and_transport(net_child.process_id)
1550 .exit_context(Exit::TubeTransporterInit, "failed to initialize tube")?;
1551
1552 vhost_user_main_tube.set_target_pid(net_child.alias_pid);
1553 cfg.net_vhost_user_tube = Some(vhost_user_main_tube);
1554
1555 Ok(net_child)
1556 }
1557
1558 /// Create backend and VMM configurations for the sound device.
1559 #[cfg(feature = "audio")]
platform_create_snd( cfg: &Config, main_child: &mut ChildProcess, exit_events: &mut Vec<Event>, ) -> Result<SndSplitConfig>1560 fn platform_create_snd(
1561 cfg: &Config,
1562 main_child: &mut ChildProcess,
1563 exit_events: &mut Vec<Event>,
1564 ) -> Result<SndSplitConfig> {
1565 let exit_event = Event::new().exit_context(Exit::CreateEvent, "failed to create exit event")?;
1566 exit_events.push(
1567 exit_event
1568 .try_clone()
1569 .exit_context(Exit::CloneEvent, "failed to clone event")?,
1570 );
1571
1572 let (backend_config_product, vmm_config_product) =
1573 get_snd_product_configs(cfg, main_child.alias_pid)?;
1574
1575 let parameters = SndParameters {
1576 backend: "winaudio".try_into().unwrap(),
1577 num_input_devices: num_input_sound_devices(cfg),
1578 num_input_streams: num_input_sound_streams(cfg),
1579 ..Default::default()
1580 };
1581
1582 let backend_config = Some(SndBackendConfig {
1583 device_vhost_user_tube: None,
1584 exit_event,
1585 parameters,
1586 product_config: backend_config_product,
1587 });
1588
1589 let vmm_config = Some(SndVmmConfig {
1590 main_vhost_user_tube: None,
1591 product_config: vmm_config_product,
1592 });
1593
1594 Ok(SndSplitConfig {
1595 backend_config,
1596 vmm_config,
1597 })
1598 }
1599
1600 /// Returns a snd child process for vhost-user sound.
1601 #[cfg(feature = "audio")]
start_up_snd( cfg: &mut Config, log_args: &LogArgs, mut snd_cfg: SndSplitConfig, main_child: &mut ChildProcess, children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, metric_tubes: &mut Vec<RecvTube>, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<ChildProcess>1602 fn start_up_snd(
1603 cfg: &mut Config,
1604 log_args: &LogArgs,
1605 mut snd_cfg: SndSplitConfig,
1606 main_child: &mut ChildProcess,
1607 children: &mut HashMap<u32, ChildCleanup>,
1608 wait_ctx: &mut WaitContext<Token>,
1609 metric_tubes: &mut Vec<RecvTube>,
1610 #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
1611 ) -> Result<ChildProcess> {
1612 // Extract the backend config from the sound config, so it can run elsewhere.
1613 let mut backend_cfg = snd_cfg
1614 .backend_config
1615 .take()
1616 .expect("snd backend config must be set");
1617
1618 let (mut main_vhost_user_tube, mut device_host_user_tube) =
1619 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
1620
1621 let snd_child = spawn_child(
1622 current_exe().unwrap().to_str().unwrap(),
1623 ["device", "snd"],
1624 get_log_path(cfg, "snd_stdout.log"),
1625 get_log_path(cfg, "snd_stderr.log"),
1626 ProcessType::Snd,
1627 children,
1628 wait_ctx,
1629 /* skip_bootstrap= */
1630 #[cfg(test)]
1631 false,
1632 /* use_sandbox= */
1633 cfg.jail_config.is_some(),
1634 vec![],
1635 cfg,
1636 )?;
1637
1638 snd_child
1639 .tube_transporter
1640 .serialize_and_transport(snd_child.process_id)
1641 .exit_context(Exit::TubeTransporterInit, "failed to initialize tube")?;
1642
1643 // Update target PIDs to new child.
1644 device_host_user_tube.set_target_pid(main_child.alias_pid);
1645 main_vhost_user_tube.set_target_pid(snd_child.alias_pid);
1646
1647 // Insert vhost-user tube to backend / frontend configs.
1648 backend_cfg.device_vhost_user_tube = Some(device_host_user_tube);
1649 if let Some(vmm_config) = snd_cfg.vmm_config.as_mut() {
1650 vmm_config.main_vhost_user_tube = Some(main_vhost_user_tube);
1651 }
1652
1653 // Send VMM config to main process.
1654 cfg.snd_split_config = Some(snd_cfg);
1655
1656 let startup_args = CommonChildStartupArgs::new(
1657 log_args,
1658 get_log_path(cfg, "snd_syslog.log"),
1659 #[cfg(feature = "crash-report")]
1660 create_crash_report_attrs(cfg, product_type::SND),
1661 #[cfg(feature = "process-invariants")]
1662 process_invariants.clone(),
1663 Some(metrics_tube_pair(metric_tubes)?),
1664 )?;
1665 snd_child.bootstrap_tube.send(&startup_args).unwrap();
1666
1667 // Send backend config to Snd child.
1668 snd_child.bootstrap_tube.send(&backend_cfg).unwrap();
1669
1670 Ok(snd_child)
1671 }
1672
1673 #[cfg(feature = "gpu")]
platform_create_input_event_config(cfg: &Config) -> Result<InputEventSplitConfig>1674 fn platform_create_input_event_config(cfg: &Config) -> Result<InputEventSplitConfig> {
1675 let mut event_devices = vec![];
1676 let mut multi_touch_pipes = vec![];
1677 let mut mouse_pipes = vec![];
1678 let mut keyboard_pipes = vec![];
1679
1680 for input in &cfg.virtio_input {
1681 match input {
1682 InputDeviceOption::MultiTouch { .. } => {
1683 let (event_device_pipe, virtio_input_pipe) =
1684 StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Byte)
1685 .exit_context(Exit::EventDeviceSetup, "failed to set up EventDevice")?;
1686 event_devices.push(EventDevice::touchscreen(event_device_pipe));
1687 multi_touch_pipes.push(virtio_input_pipe);
1688 }
1689 InputDeviceOption::Mouse { .. } => {
1690 let (event_device_pipe, virtio_input_pipe) =
1691 StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Byte)
1692 .exit_context(Exit::EventDeviceSetup, "failed to set up EventDevice")?;
1693 event_devices.push(EventDevice::mouse(event_device_pipe));
1694 mouse_pipes.push(virtio_input_pipe);
1695 }
1696 _ => {}
1697 }
1698 }
1699
1700 // One keyboard
1701 let (event_device_pipe, virtio_input_pipe) =
1702 StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Byte)
1703 .exit_context(Exit::EventDeviceSetup, "failed to set up EventDevice")?;
1704 event_devices.push(EventDevice::keyboard(event_device_pipe));
1705 keyboard_pipes.push(virtio_input_pipe);
1706
1707 Ok(InputEventSplitConfig {
1708 backend_config: Some(InputEventBackendConfig { event_devices }),
1709 vmm_config: InputEventVmmConfig {
1710 multi_touch_pipes,
1711 mouse_pipes,
1712 keyboard_pipes,
1713 },
1714 })
1715 }
1716
1717 #[cfg(feature = "gpu")]
1718 /// Create Window procedure thread configurations.
platform_create_window_procedure_thread_configs( cfg: &Config, mut wndproc_thread_builder: WindowProcedureThreadBuilder, main_alias_pid: u32, device_alias_pid: u32, ) -> Result<WindowProcedureThreadSplitConfig>1719 fn platform_create_window_procedure_thread_configs(
1720 cfg: &Config,
1721 mut wndproc_thread_builder: WindowProcedureThreadBuilder,
1722 main_alias_pid: u32,
1723 device_alias_pid: u32,
1724 ) -> Result<WindowProcedureThreadSplitConfig> {
1725 let product_config = get_window_procedure_thread_product_configs(
1726 cfg,
1727 &mut wndproc_thread_builder,
1728 main_alias_pid,
1729 device_alias_pid,
1730 )
1731 .context("create product window procedure thread configs")?;
1732 Ok(WindowProcedureThreadSplitConfig {
1733 wndproc_thread_builder: Some(wndproc_thread_builder),
1734 vmm_config: WindowProcedureThreadVmmConfig { product_config },
1735 })
1736 }
1737
1738 #[cfg(feature = "gpu")]
1739 /// Create backend and VMM configurations for the GPU device.
platform_create_gpu( cfg: &Config, #[allow(unused_variables)] main_child: &mut ChildProcess, exit_events: &mut Vec<Event>, exit_evt_wrtube: SendTube, ) -> Result<Option<(GpuBackendConfig, GpuVmmConfig)>>1740 fn platform_create_gpu(
1741 cfg: &Config,
1742 #[allow(unused_variables)] main_child: &mut ChildProcess,
1743 exit_events: &mut Vec<Event>,
1744 exit_evt_wrtube: SendTube,
1745 ) -> Result<Option<(GpuBackendConfig, GpuVmmConfig)>> {
1746 if cfg.gpu_parameters.is_none() {
1747 return Ok(None);
1748 }
1749 let exit_event = Event::new().exit_context(Exit::CreateEvent, "failed to create exit event")?;
1750 exit_events.push(
1751 exit_event
1752 .try_clone()
1753 .exit_context(Exit::CloneEvent, "failed to clone event")?,
1754 );
1755
1756 let (backend_config_product, vmm_config_product) =
1757 get_gpu_product_configs(cfg, main_child.alias_pid)?;
1758
1759 let (mut main_vhost_user_tube, mut device_host_user_tube) =
1760 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
1761 // Start off the vhost-user tube assuming it is in the main process.
1762 main_vhost_user_tube.set_target_pid(main_child.alias_pid);
1763 device_host_user_tube.set_target_pid(main_child.alias_pid);
1764
1765 let (mut gpu_control_host_tube, mut gpu_control_device_tube) =
1766 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
1767 gpu_control_host_tube.set_target_pid(main_child.alias_pid);
1768 gpu_control_device_tube.set_target_pid(main_child.alias_pid);
1769
1770 let backend_config = GpuBackendConfig {
1771 device_vhost_user_tube: Some(device_host_user_tube),
1772 exit_event,
1773 exit_evt_wrtube,
1774 gpu_control_device_tube,
1775 params: cfg
1776 .gpu_parameters
1777 .as_ref()
1778 .expect("missing GpuParameters in config")
1779 .clone(),
1780 product_config: backend_config_product,
1781 };
1782
1783 let vmm_config = GpuVmmConfig {
1784 main_vhost_user_tube: Some(main_vhost_user_tube),
1785 gpu_control_host_tube: Some(gpu_control_host_tube),
1786 product_config: vmm_config_product,
1787 };
1788
1789 Ok(Some((backend_config, vmm_config)))
1790 }
1791
1792 #[cfg(feature = "gpu")]
1793 /// Returns a gpu child process for vhost-user GPU.
start_up_gpu( cfg: &mut Config, log_args: &LogArgs, gpu_cfg: (GpuBackendConfig, GpuVmmConfig), input_event_cfg: &mut InputEventSplitConfig, main_child: &mut ChildProcess, children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, metric_tubes: &mut Vec<RecvTube>, wndproc_thread_builder: WindowProcedureThreadBuilder, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<ChildProcess>1794 fn start_up_gpu(
1795 cfg: &mut Config,
1796 log_args: &LogArgs,
1797 gpu_cfg: (GpuBackendConfig, GpuVmmConfig),
1798 input_event_cfg: &mut InputEventSplitConfig,
1799 main_child: &mut ChildProcess,
1800 children: &mut HashMap<u32, ChildCleanup>,
1801 wait_ctx: &mut WaitContext<Token>,
1802 metric_tubes: &mut Vec<RecvTube>,
1803 wndproc_thread_builder: WindowProcedureThreadBuilder,
1804 #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants,
1805 ) -> Result<ChildProcess> {
1806 let (backend_cfg, mut vmm_cfg) = gpu_cfg;
1807
1808 let gpu_child = spawn_child(
1809 current_exe().unwrap().to_str().unwrap(),
1810 ["device", "gpu"],
1811 get_log_path(cfg, "gpu_stdout.log"),
1812 get_log_path(cfg, "gpu_stderr.log"),
1813 ProcessType::Gpu,
1814 children,
1815 wait_ctx,
1816 /* skip_bootstrap= */
1817 #[cfg(test)]
1818 false,
1819 /* use_sandbox= */
1820 cfg.jail_config.is_some(),
1821 vec![],
1822 cfg,
1823 )?;
1824
1825 gpu_child
1826 .tube_transporter
1827 .serialize_and_transport(gpu_child.process_id)
1828 .exit_context(Exit::TubeTransporterInit, "failed to initialize tube")?;
1829
1830 let mut wndproc_thread_cfg = platform_create_window_procedure_thread_configs(
1831 cfg,
1832 wndproc_thread_builder,
1833 main_child.alias_pid,
1834 gpu_child.alias_pid,
1835 )
1836 .context("failed to create window procedure thread configs")?;
1837 let wndproc_thread_builder = wndproc_thread_cfg
1838 .wndproc_thread_builder
1839 .take()
1840 .expect("The window procedure thread builder is missing");
1841 cfg.window_procedure_thread_split_config = Some(wndproc_thread_cfg);
1842
1843 // Update target PIDs to new child.
1844 if let Some(tube) = &mut vmm_cfg.main_vhost_user_tube {
1845 tube.set_target_pid(gpu_child.alias_pid);
1846 }
1847 if let Some(tube) = &mut vmm_cfg.gpu_control_host_tube {
1848 tube.set_target_pid(gpu_child.alias_pid);
1849 }
1850
1851 // Send VMM config to main process. Note we don't set gpu_backend_config and
1852 // input_event_backend_config, since it is passed to the child.
1853 cfg.gpu_vmm_config = Some(vmm_cfg);
1854 let input_event_backend_config = input_event_cfg
1855 .backend_config
1856 .take()
1857 .context("input event backend config is missing.")?;
1858
1859 let startup_args = CommonChildStartupArgs::new(
1860 log_args,
1861 get_log_path(cfg, "gpu_syslog.log"),
1862 #[cfg(feature = "crash-report")]
1863 create_crash_report_attrs(cfg, product_type::GPU),
1864 #[cfg(feature = "process-invariants")]
1865 process_invariants.clone(),
1866 Some(metrics_tube_pair(metric_tubes)?),
1867 )?;
1868 gpu_child.bootstrap_tube.send(&startup_args).unwrap();
1869
1870 // Send backend config to GPU child.
1871 gpu_child
1872 .bootstrap_tube
1873 .send(&(
1874 backend_cfg,
1875 input_event_backend_config,
1876 wndproc_thread_builder,
1877 ))
1878 .unwrap();
1879
1880 Ok(gpu_child)
1881 }
1882
1883 /// Spawns a child process, sending it a control tube as the --bootstrap=HANDLE_NUMBER argument.
1884 /// stdout & stderr are redirected to the provided file paths.
spawn_child<I, S>( program: &str, args: I, stdout_path: Option<PathBuf>, stderr_path: Option<PathBuf>, process_type: ProcessType, children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, #[cfg(test)] skip_bootstrap: bool, use_sandbox: bool, mut tubes: Vec<TubeTransferData>, #[allow(unused_variables)] cfg: &Config, ) -> Result<ChildProcess> where I: IntoIterator<Item = S>, S: AsRef<OsStr>,1885 fn spawn_child<I, S>(
1886 program: &str,
1887 args: I,
1888 stdout_path: Option<PathBuf>,
1889 stderr_path: Option<PathBuf>,
1890 process_type: ProcessType,
1891 children: &mut HashMap<u32, ChildCleanup>,
1892 wait_ctx: &mut WaitContext<Token>,
1893 #[cfg(test)] skip_bootstrap: bool,
1894 use_sandbox: bool,
1895 mut tubes: Vec<TubeTransferData>,
1896 #[allow(unused_variables)] cfg: &Config,
1897 ) -> Result<ChildProcess>
1898 where
1899 I: IntoIterator<Item = S>,
1900 S: AsRef<OsStr>,
1901 {
1902 let (tube_transport_pipe, tube_transport_main_child) = named_pipes::pair(
1903 &FramingMode::Message.into(),
1904 &BlockingMode::Blocking.into(),
1905 /* timeout= */ 0,
1906 )
1907 .exit_context(Exit::CreateSocket, "failed to create socket")?;
1908
1909 let stdout_file = if let Some(path) = stdout_path {
1910 Some(
1911 OpenOptions::new()
1912 .append(true)
1913 .create(true)
1914 .open(path.as_path())
1915 .with_exit_context(Exit::LogFile, || {
1916 format!("failed to open log file {}", path.display())
1917 })?,
1918 )
1919 } else {
1920 None
1921 };
1922
1923 let stderr_file = if let Some(path) = stderr_path {
1924 Some(
1925 OpenOptions::new()
1926 .append(true)
1927 .create(true)
1928 .open(path.as_path())
1929 .with_exit_context(Exit::LogFile, || {
1930 format!("failed to open log file {}", path.display())
1931 })?,
1932 )
1933 } else {
1934 None
1935 };
1936
1937 #[cfg(test)]
1938 let bootstrap = if !skip_bootstrap {
1939 vec![
1940 "--bootstrap".to_string(),
1941 (tube_transport_main_child.as_raw_descriptor() as usize).to_string(),
1942 ]
1943 } else {
1944 vec![]
1945 };
1946 #[cfg(not(test))]
1947 let bootstrap = vec![
1948 "--bootstrap".to_string(),
1949 (tube_transport_main_child.as_raw_descriptor() as usize).to_string(),
1950 ];
1951
1952 let input_args: Vec<S> = args.into_iter().collect();
1953 let args = input_args
1954 .iter()
1955 .map(|arg| arg.as_ref())
1956 .chain(bootstrap.iter().map(|arg| arg.as_ref()));
1957
1958 #[cfg(feature = "sandbox")]
1959 let (process_id, child) = if use_sandbox {
1960 spawn_sandboxed_child(
1961 program,
1962 args,
1963 stdout_file,
1964 stderr_file,
1965 vec![&tube_transport_main_child],
1966 process_policy(process_type, cfg),
1967 )?
1968 } else {
1969 spawn_unsandboxed_child(
1970 program,
1971 args,
1972 stdout_file,
1973 stderr_file,
1974 vec![&tube_transport_main_child],
1975 )?
1976 };
1977 #[cfg(not(feature = "sandbox"))]
1978 let (process_id, child) = spawn_unsandboxed_child(
1979 program,
1980 args,
1981 stdout_file,
1982 stderr_file,
1983 vec![&tube_transport_main_child],
1984 )?;
1985
1986 let (mut bootstrap_tube, bootstrap_tube_child) =
1987 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
1988
1989 // Make sure our end of the Tube knows the PID of the child end.
1990 bootstrap_tube.set_target_pid(process_id);
1991
1992 tubes.push(TubeTransferData {
1993 tube: bootstrap_tube_child,
1994 tube_token: TubeToken::Bootstrap,
1995 });
1996
1997 let (dh_tube, dh_tube_child, alias_pid) = if use_sandbox {
1998 let (broker, child) =
1999 Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?;
2000 (Some(broker), Some(child), rand::random())
2001 } else {
2002 (None, None, process_id)
2003 };
2004
2005 let tube_transporter =
2006 TubeTransporter::new(tube_transport_pipe, tubes, Some(alias_pid), dh_tube_child);
2007
2008 // Register this child to be waited upon.
2009 let process_handle = Descriptor(child.as_raw_descriptor());
2010 wait_ctx
2011 .add(&process_handle, Token::Process(alias_pid))
2012 .exit_context(
2013 Exit::WaitContextAdd,
2014 "failed to add trigger to event context",
2015 )?;
2016
2017 children.insert(
2018 alias_pid,
2019 ChildCleanup {
2020 process_type,
2021 child,
2022 dh_tube,
2023 },
2024 );
2025
2026 if use_sandbox {
2027 wait_ctx
2028 .add(
2029 children[&alias_pid]
2030 .dh_tube
2031 .as_ref()
2032 .unwrap()
2033 .get_read_notifier(),
2034 Token::DuplicateHandle(alias_pid),
2035 )
2036 .exit_context(
2037 Exit::WaitContextAdd,
2038 "failed to add trigger to event context",
2039 )?;
2040 }
2041
2042 Ok(ChildProcess {
2043 bootstrap_tube,
2044 tube_transporter,
2045 process_id,
2046 alias_pid,
2047 })
2048 }
2049
2050 #[cfg(test)]
2051 mod tests {
2052 use base::thread::spawn_with_timeout;
2053
2054 use super::*;
2055
2056 /// Verifies that the supervisor loop exits normally with a single child that exits.
2057 #[test]
smoke_test()2058 fn smoke_test() {
2059 spawn_with_timeout(|| {
2060 let mut children: HashMap<u32, ChildCleanup> = HashMap::new();
2061 let mut wait_ctx: WaitContext<Token> = WaitContext::new().unwrap();
2062 let exit_events = vec![Event::new().unwrap()];
2063 let _child_main = spawn_child(
2064 "ping",
2065 ["127.0.0.1", "-n", "2"],
2066 None,
2067 None,
2068 ProcessType::Main,
2069 &mut children,
2070 &mut wait_ctx,
2071 /* skip_bootstrap= */ true,
2072 /* use_sandbox= */ false,
2073 Vec::new(),
2074 &Config::default(),
2075 );
2076
2077 Supervisor::broker_supervise_loop(children, wait_ctx, exit_events).unwrap();
2078 })
2079 .try_join(Duration::from_secs(5))
2080 .unwrap();
2081 }
2082
2083 /// Verifies that the supervisor loop exits normally when a device exits first, and then
2084 /// the main loop exits.
2085 #[test]
main_and_device_clean_exit()2086 fn main_and_device_clean_exit() {
2087 spawn_with_timeout(|| {
2088 let mut children: HashMap<u32, ChildCleanup> = HashMap::new();
2089 let mut wait_ctx: WaitContext<Token> = WaitContext::new().unwrap();
2090 let exit_events = vec![Event::new().unwrap()];
2091 let _child_main = spawn_child(
2092 "ping",
2093 ["127.0.0.1", "-n", "4"],
2094 None,
2095 None,
2096 ProcessType::Main,
2097 &mut children,
2098 &mut wait_ctx,
2099 /* skip_bootstrap= */ true,
2100 /* use_sandbox= */ false,
2101 Vec::new(),
2102 &Config::default(),
2103 );
2104 let _child_device = spawn_child(
2105 "ping",
2106 ["127.0.0.1", "-n", "2"],
2107 None,
2108 None,
2109 ProcessType::Block,
2110 &mut children,
2111 &mut wait_ctx,
2112 /* skip_bootstrap= */ true,
2113 /* use_sandbox= */ false,
2114 Vec::new(),
2115 &Config::default(),
2116 );
2117
2118 Supervisor::broker_supervise_loop(children, wait_ctx, exit_events).unwrap();
2119 })
2120 .try_join(Duration::from_secs(5))
2121 .unwrap();
2122 }
2123
2124 /// Verifies that the supervisor loop ends even if a device takes too long to exit.
2125 #[test]
device_takes_too_long_to_exit()2126 fn device_takes_too_long_to_exit() {
2127 spawn_with_timeout(|| {
2128 let mut children: HashMap<u32, ChildCleanup> = HashMap::new();
2129 let mut wait_ctx: WaitContext<Token> = WaitContext::new().unwrap();
2130 let exit_events = vec![Event::new().unwrap()];
2131 let _child_main = spawn_child(
2132 "ping",
2133 ["127.0.0.1", "-n", "2"],
2134 None,
2135 None,
2136 ProcessType::Main,
2137 &mut children,
2138 &mut wait_ctx,
2139 /* skip_bootstrap= */ true,
2140 /* use_sandbox= */ false,
2141 Vec::new(),
2142 &Config::default(),
2143 );
2144 let _child_device = spawn_child(
2145 "ping",
2146 ["127.0.0.1", "-n", "11"],
2147 None,
2148 None,
2149 ProcessType::Block,
2150 &mut children,
2151 &mut wait_ctx,
2152 /* skip_bootstrap= */ true,
2153 /* use_sandbox= */ false,
2154 Vec::new(),
2155 &Config::default(),
2156 );
2157
2158 assert_eq!(
2159 Supervisor::broker_supervise_loop(children, wait_ctx, exit_events)
2160 .to_exit_code()
2161 .unwrap(),
2162 ExitCode::from(Exit::BrokerMainExitedTimeout),
2163 );
2164 })
2165 .try_join(Duration::from_secs(10))
2166 .unwrap();
2167 }
2168
2169 /// Verifies that the supervisor loop ends even if the main process takes too long to exit.
2170 #[test]
main_takes_too_long_to_exit()2171 fn main_takes_too_long_to_exit() {
2172 spawn_with_timeout(|| {
2173 let mut children: HashMap<u32, ChildCleanup> = HashMap::new();
2174 let mut wait_ctx: WaitContext<Token> = WaitContext::new().unwrap();
2175 let exit_events = vec![Event::new().unwrap()];
2176 let _child_main = spawn_child(
2177 "ping",
2178 ["127.0.0.1", "-n", "11"],
2179 None,
2180 None,
2181 ProcessType::Main,
2182 &mut children,
2183 &mut wait_ctx,
2184 /* skip_bootstrap= */ true,
2185 /* use_sandbox= */ false,
2186 Vec::new(),
2187 &Config::default(),
2188 );
2189 let _child_device = spawn_child(
2190 "ping",
2191 ["127.0.0.1", "-n", "2"],
2192 None,
2193 None,
2194 ProcessType::Block,
2195 &mut children,
2196 &mut wait_ctx,
2197 /* skip_bootstrap= */ true,
2198 /* use_sandbox= */ false,
2199 Vec::new(),
2200 &Config::default(),
2201 );
2202
2203 assert_eq!(
2204 Supervisor::broker_supervise_loop(children, wait_ctx, exit_events)
2205 .to_exit_code()
2206 .unwrap(),
2207 ExitCode::from(Exit::BrokerDeviceExitedTimeout),
2208 );
2209 })
2210 .try_join(Duration::from_secs(10))
2211 .unwrap();
2212 }
2213
2214 /// Verifies that the supervisor loop ends even if a device takes too long to exit.
2215 #[test]
device_crash_returns_child_error()2216 fn device_crash_returns_child_error() {
2217 spawn_with_timeout(|| {
2218 let mut children: HashMap<u32, ChildCleanup> = HashMap::new();
2219 let mut wait_ctx: WaitContext<Token> = WaitContext::new().unwrap();
2220 let exit_events = vec![Event::new().unwrap()];
2221 let _child_main = spawn_child(
2222 "ping",
2223 ["127.0.0.1", "-n", "2"],
2224 None,
2225 None,
2226 ProcessType::Main,
2227 &mut children,
2228 &mut wait_ctx,
2229 /* skip_bootstrap= */ true,
2230 /* use_sandbox= */ false,
2231 Vec::new(),
2232 &Config::default(),
2233 );
2234 let _child_device = spawn_child(
2235 "cmd",
2236 ["/c", "exit -1"],
2237 None,
2238 None,
2239 ProcessType::Block,
2240 &mut children,
2241 &mut wait_ctx,
2242 /* skip_bootstrap= */ true,
2243 /* use_sandbox= */ false,
2244 Vec::new(),
2245 &Config::default(),
2246 );
2247
2248 assert_eq!(
2249 Supervisor::broker_supervise_loop(children, wait_ctx, exit_events)
2250 .to_exit_code()
2251 .unwrap(),
2252 (to_process_type_error(-1i32 as u32, ProcessType::Block) as i32),
2253 );
2254 })
2255 .try_join(Duration::from_secs(10))
2256 .unwrap();
2257 }
2258
2259 /// Verifies that sigterm makes the supervisor loop signal the exit event.
2260 #[test]
sigterm_signals_exit_event()2261 fn sigterm_signals_exit_event() {
2262 let exit_event = Event::new().unwrap();
2263 let exit_event_copy = exit_event.try_clone().unwrap();
2264
2265 spawn_with_timeout(move || {
2266 let sigterm_event = Event::new().unwrap();
2267 let mut wait_ctx: WaitContext<Token> = WaitContext::new().unwrap();
2268 let mut children: HashMap<u32, ChildCleanup> = HashMap::new();
2269 let _child_main = spawn_child(
2270 "ping",
2271 ["127.0.0.1", "-n", "3"],
2272 None,
2273 None,
2274 ProcessType::Main,
2275 &mut children,
2276 &mut wait_ctx,
2277 /* skip_bootstrap= */ true,
2278 /* use_sandbox= */ false,
2279 Vec::new(),
2280 &Config::default(),
2281 );
2282 wait_ctx.add(&sigterm_event, Token::Sigterm).unwrap();
2283 sigterm_event.signal().unwrap();
2284
2285 assert_eq!(
2286 Supervisor::broker_supervise_loop(children, wait_ctx, vec![exit_event_copy])
2287 .to_exit_code()
2288 .unwrap(),
2289 ExitCode::from(Exit::KilledBySignal),
2290 );
2291 })
2292 .try_join(Duration::from_secs(10))
2293 .unwrap();
2294
2295 exit_event.wait_timeout(Duration::from_secs(0)).unwrap();
2296 }
2297 }
2298