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