• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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