1use crate::{
6 Features, MountAction, expose_root, parse_features, parse_numbered_handles,
7 run_container_features, serve_component_runner, serve_container_controller,
8 serve_container_info, serve_graphical_presenter, serve_lutex_controller,
9};
10use anyhow::{Context, Error, anyhow, bail};
11use bootreason::{get_bootloader_file_bootreason, get_or_init_android_bootreason};
12use bstr::{BString, ByteSlice};
13use devicetree::parser::parse_devicetree;
14use devicetree::types::Devicetree;
15use fidl::endpoints::{RequestStream, ServerEnd};
16use fidl_fuchsia_boot as fboot;
17use fidl_fuchsia_component as fcomponent;
18use fidl_fuchsia_component_runner as frunner;
19use fidl_fuchsia_component_runner::{TaskProviderRequest, TaskProviderRequestStream};
20use fidl_fuchsia_element as felement;
21use fidl_fuchsia_feedback::CrashReporterMarker;
22use fidl_fuchsia_io as fio;
23use fidl_fuchsia_mem as fmem;
24use fidl_fuchsia_memory_attribution as fattribution;
25use fidl_fuchsia_starnix_binder as fbinder;
26use fidl_fuchsia_starnix_container as fstarcontainer;
27use fidl_fuchsia_sysinfo as fsysinfo;
28use fidl_fuchsia_time_external::AdjustMarker;
29use fuchsia_async as fasync;
30use fuchsia_async::DurationExt;
31use fuchsia_component::client::{connect_to_protocol, connect_to_protocol_sync};
32use fuchsia_component::server::ServiceFs;
33use fuchsia_inspect as inspect;
34use fuchsia_runtime as fruntime;
35use futures::channel::oneshot;
36use futures::{FutureExt, StreamExt, TryStreamExt};
37use serde::Deserialize;
38use starnix_container_structured_config::Config as ContainerStructuredConfig;
39use starnix_core::device::remote_block_device::remote_block_device_init;
40use starnix_core::execution::{
41 create_init_process, create_system_task, execute_task_with_prerun_result,
42};
43use starnix_core::fs::fuchsia::new_remotefs_in_root;
44use starnix_core::fs::tmpfs::TmpFs;
45use starnix_core::security;
46use starnix_core::task::container_namespace::ContainerNamespace;
47use starnix_core::task::{
48 CurrentTask, ExitStatus, Kernel, OomWatermarksMb, RoleOverrides, SchedulerManager,
49 parse_cmdline,
50};
51use starnix_core::vfs::{FileSystemOptions, LookupContext, Namespace, WhatToMount};
52use starnix_logging::{
53 CATEGORY_STARNIX, NAME_CREATE_CONTAINER, log_debug, log_error, log_info, log_warn,
54};
55use starnix_modules::{
56 LayeredFsBuilder, OverlayStack, init_common_devices, register_common_file_systems,
57 register_common_syscalls,
58};
59
60use starnix_task_command::TaskCommand;
61use starnix_uapi::errors::{ENOENT, SourceContext};
62use starnix_uapi::open_flags::OpenFlags;
63use starnix_uapi::resource_limits::Resource;
64use starnix_uapi::{errno, tid_t};
65use std::ffi::CString;
66
67use std::sync::Arc;
68use zx::Task as _;
69
70use std::sync::Weak;
71
72use crate::serve_memory_attribution_provider_container;
73use attribution_server::{AttributionServer, AttributionServerHandle};
74
75struct ContainerMemoryAttributionManager {
77 memory_attribution_server: AttributionServerHandle,
79}
80
81impl ContainerMemoryAttributionManager {
82 pub fn new(kernel: Weak<Kernel>, component_instance: zx::Event) -> Self {
85 let memory_attribution_server = AttributionServer::new(Box::new(move || {
86 let kernel_ref = match kernel.upgrade() {
87 None => return vec![],
88 Some(k) => k,
89 };
90 attribution_info_for_kernel(kernel_ref.as_ref(), &component_instance)
91 }));
92
93 ContainerMemoryAttributionManager { memory_attribution_server }
94 }
95
96 pub fn new_observer(
98 &self,
99 control_handle: fattribution::ProviderControlHandle,
100 ) -> attribution_server::Observer {
101 self.memory_attribution_server.new_observer(control_handle)
102 }
103}
104
105fn attribution_info_for_kernel(
109 kernel: &Kernel,
110 component_instance: &zx::Event,
111) -> Vec<fattribution::AttributionUpdate> {
112 let (client_end, server_end) =
116 fidl::endpoints::create_request_stream::<fattribution::ProviderMarker>();
117 fuchsia_async::Task::spawn(serve_memory_attribution_provider_container(server_end, kernel))
118 .detach();
119
120 let starnix_kernel_id = Some(1);
121 let starnix_kernel_principal = fattribution::NewPrincipal {
122 identifier: starnix_kernel_id,
123 description: Some(fattribution::Description::Part("starnix_kernel".to_string())),
124 principal_type: Some(fattribution::PrincipalType::Part),
125 detailed_attribution: None,
129 ..Default::default()
130 };
131
132 let starnix_kernel_attribution = fattribution::UpdatedPrincipal {
133 identifier: starnix_kernel_id, resources: Some(fattribution::Resources::Data(fattribution::Data {
135 resources: vec![fattribution::Resource::ProcessMapped(fattribution::ProcessMapped {
136 process: fuchsia_runtime::process_self().koid().unwrap().raw_koid(),
137 base: 0, len: u64::MAX,
139 hint_skip_handle_table: false,
140 })],
141 })),
142 ..Default::default()
143 };
144
145 let container_id = Some(2);
146 let new_principal = fattribution::NewPrincipal {
147 identifier: container_id,
148 description: Some(fattribution::Description::Component(
149 component_instance.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
150 )),
151 principal_type: Some(fattribution::PrincipalType::Runnable),
152 detailed_attribution: Some(client_end),
153 ..Default::default()
154 };
155 let attribution = fattribution::UpdatedPrincipal {
156 identifier: container_id,
157 resources: Some(fattribution::Resources::Data(fattribution::Data {
158 resources: vec![fattribution::Resource::KernelObject(
159 fuchsia_runtime::job_default().koid().unwrap().raw_koid(),
160 )],
161 })),
162 ..Default::default()
163 };
164
165 vec![
166 fattribution::AttributionUpdate::Add(new_principal),
167 fattribution::AttributionUpdate::Add(starnix_kernel_principal),
168 fattribution::AttributionUpdate::Update(attribution),
169 fattribution::AttributionUpdate::Update(starnix_kernel_attribution),
170 ]
171}
172
173#[derive(Debug)]
174pub struct ContainerStartInfo {
175 pub program: ContainerProgram,
177
178 pub config: ContainerStructuredConfig,
179
180 outgoing_dir: Option<zx::Channel>,
184
185 pub container_namespace: ContainerNamespace,
188
189 runtime_dir: Option<ServerEnd<fio::DirectoryMarker>>,
191
192 break_on_start: Option<zx::EventPair>,
194
195 component_instance: Option<zx::Event>,
198}
199
200const MISSING_CONFIG_VMO_CONTEXT: &str = concat!(
201 "Retrieving container config VMO. ",
202 "If this fails, make sure your container CML includes ",
203 "//src/starnix/containers/container.shard.cml.",
204);
205
206impl ContainerStartInfo {
207 fn new(mut start_info: frunner::ComponentStartInfo) -> Result<Self, Error> {
208 let program = start_info.program.as_ref().context("retrieving program block")?;
209 let program: ContainerProgram =
210 runner::serde::deserialize_program(&program).context("parsing program block")?;
211
212 let encoded_config =
213 start_info.encoded_config.as_ref().context(MISSING_CONFIG_VMO_CONTEXT)?;
214 let config = match encoded_config {
215 fmem::Data::Bytes(b) => ContainerStructuredConfig::from_bytes(b),
216 fmem::Data::Buffer(b) => ContainerStructuredConfig::from_vmo(&b.vmo),
217 other => anyhow::bail!("unknown Data variant {other:?}"),
218 }
219 .context("parsing container structured config")?;
220
221 let ns = start_info.ns.take().context("retrieving container namespace")?;
222 let container_namespace = ContainerNamespace::from(ns);
223
224 let outgoing_dir = start_info.outgoing_dir.take().map(|dir| dir.into_channel());
225 let component_instance = start_info.component_instance;
226
227 Ok(Self {
228 program,
229 config,
230 outgoing_dir,
231 container_namespace,
232 component_instance,
233 break_on_start: start_info.break_on_start,
234 runtime_dir: start_info.runtime_dir,
235 })
236 }
237
238 #[cfg(test)]
239 pub(crate) fn new_for_test(
240 features: Vec<String>,
241 modules: Vec<String>,
242 config: ContainerStructuredConfig,
243 ) -> Self {
244 Self {
245 program: ContainerProgram { features, modules, ..Default::default() },
246 config,
247 outgoing_dir: None,
248 container_namespace: ContainerNamespace::new(),
249 runtime_dir: None,
250 break_on_start: None,
251 component_instance: None,
252 }
253 }
254}
255
256#[derive(Debug, Default, Deserialize)]
257#[serde(deny_unknown_fields)]
258pub struct ContainerProgram {
259 name: String,
261
262 init: Vec<String>,
264
265 #[serde(default)]
267 kernel_cmdline: String,
268
269 #[serde(default)]
271 pub mounts: Vec<String>,
272
273 #[serde(default)]
275 pub features: Vec<String>,
276
277 #[serde(default)]
279 pub modules: Vec<String>,
280
281 #[serde(default)]
283 rlimits: Vec<String>,
284
285 #[serde(default)]
287 startup_file_path: String,
288
289 #[serde(default)]
293 pub default_seclabel: Option<String>,
294
295 #[serde(default = "default_uid")]
299 pub default_uid: runner::serde::StoreAsString<u32>,
300
301 pub default_ns_mount_options: Option<Vec<String>>,
305
306 #[serde(default)]
317 task_role_overrides: Vec<TaskSchedulerMapping>,
318
319 #[serde(default)]
323 pub stop_last_on_shutdown: Vec<String>,
324}
325
326#[derive(Default, Deserialize)]
329struct TaskSchedulerMapping {
330 role: String,
332 process: String,
334 thread: String,
336 cgroup: Option<String>,
338 is_leader: Option<runner::serde::StoreAsString<bool>>,
340 min_nice: Option<runner::serde::StoreAsString<i8>>,
342 max_nice: Option<runner::serde::StoreAsString<i8>>,
344}
345
346impl std::fmt::Debug for TaskSchedulerMapping {
347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348 write!(
349 f,
350 "process `{}` thread `{}` cgroup `{:?}` is_leader `{:?}` min_nice `{:?}` max_nice `{:?}` role `{}`",
351 self.process,
352 self.thread,
353 self.cgroup,
354 self.is_leader,
355 self.min_nice,
356 self.max_nice,
357 self.role
358 )
359 }
360}
361
362fn default_uid() -> runner::serde::StoreAsString<u32> {
363 runner::serde::StoreAsString(42)
364}
365
366fn to_cstr(str: &str) -> CString {
368 CString::new(str.to_string()).unwrap()
369}
370
371#[must_use = "The container must run serve on this config"]
372pub struct ContainerServiceConfig {
373 start_info: ContainerStartInfo,
374 request_stream: frunner::ComponentControllerRequestStream,
375 receiver: oneshot::Receiver<Result<ExitStatus, Error>>,
376}
377
378pub struct Container {
379 pub kernel: Arc<Kernel>,
381
382 memory_attribution_manager: ContainerMemoryAttributionManager,
383
384 _node: inspect::Node,
386
387 _thread_bound: std::marker::PhantomData<*mut u8>,
390}
391
392impl Container {
393 pub fn system_task(&self) -> &CurrentTask {
394 self.kernel.kthreads.system_task()
395 }
396
397 async fn serve_outgoing_directory(
398 &self,
399 outgoing_dir: Option<zx::Channel>,
400 ) -> Result<(), Error> {
401 if let Some(outgoing_dir) = outgoing_dir {
402 let mut fs = ServiceFs::new_local();
405 fs.dir("svc")
406 .add_fidl_service(ExposedServices::ComponentRunner)
407 .add_fidl_service(ExposedServices::ContainerController)
408 .add_fidl_service(ExposedServices::GraphicalPresenter)
409 .add_fidl_service(ExposedServices::LutexController)
410 .add_fidl_service(ExposedServices::ContainerInfo);
411
412 let (fs_root, fs_root_server_end) = fidl::endpoints::create_proxy();
414 fs.add_remote("fs_root", fs_root);
415 expose_root(self.system_task(), fs_root_server_end)?;
416
417 fs.serve_connection(outgoing_dir.into()).map_err(|_| errno!(EINVAL))?;
418
419 fs.for_each_concurrent(None, |request_stream| async {
420 match request_stream {
421 ExposedServices::ComponentRunner(request_stream) => {
422 match serve_component_runner(request_stream, self.system_task()).await {
423 Ok(_) => {}
424 Err(e) => {
425 log_error!("Error serving component runner: {:?}", e);
426 }
427 }
428 }
429 ExposedServices::ContainerController(request_stream) => {
430 serve_container_controller(request_stream, self.system_task())
431 .await
432 .expect("failed to start container.")
433 }
434 ExposedServices::GraphicalPresenter(request_stream) => {
435 serve_graphical_presenter(request_stream, &self.kernel)
436 .await
437 .expect("failed to start GraphicalPresenter.")
438 }
439 ExposedServices::LutexController(request_stream) => {
440 serve_lutex_controller(request_stream, self.system_task())
441 .await
442 .expect("failed to start LutexController.")
443 }
444 ExposedServices::ContainerInfo(request_stream) => {
445 serve_container_info(request_stream)
446 .await
447 .expect("failed to start ContainerInfo.")
448 }
449 }
450 })
451 .await
452 }
453 Ok(())
454 }
455
456 pub async fn serve(&self, service_config: ContainerServiceConfig) -> Result<(), Error> {
457 let (r, _) = futures::join!(
458 self.serve_outgoing_directory(service_config.start_info.outgoing_dir),
459 server_component_controller(
460 self.kernel.clone(),
461 service_config.request_stream,
462 service_config.receiver
463 )
464 );
465 r
466 }
467
468 pub fn new_memory_attribution_observer(
469 &self,
470 control_handle: fattribution::ProviderControlHandle,
471 ) -> attribution_server::Observer {
472 self.memory_attribution_manager.new_observer(control_handle)
473 }
474}
475
476enum ExposedServices {
478 ComponentRunner(frunner::ComponentRunnerRequestStream),
479 ContainerController(fstarcontainer::ControllerRequestStream),
480 GraphicalPresenter(felement::GraphicalPresenterRequestStream),
481 LutexController(fbinder::LutexControllerRequestStream),
482 ContainerInfo(fstarcontainer::InfoRequestStream),
483}
484
485type TaskResult = Result<ExitStatus, Error>;
486
487async fn server_component_controller(
488 kernel: Arc<Kernel>,
489 request_stream: frunner::ComponentControllerRequestStream,
490 task_complete: oneshot::Receiver<TaskResult>,
491) {
492 *kernel.container_control_handle.lock() = Some(request_stream.control_handle());
493
494 enum Event<T, U> {
495 Controller(T),
496 Completion(U),
497 }
498
499 let mut stream = futures::stream::select(
500 request_stream.map(Event::Controller),
501 task_complete.into_stream().map(Event::Completion),
502 );
503
504 while let Some(event) = stream.next().await {
505 match event {
506 Event::Controller(Ok(frunner::ComponentControllerRequest::Stop { .. })) => {
507 log_info!("Stopping the container.");
508 }
509 Event::Controller(Ok(frunner::ComponentControllerRequest::Kill { control_handle })) => {
510 log_info!("Killing the container's job.");
511 control_handle.shutdown_with_epitaph(zx::Status::err_from_raw(
512 fcomponent::Error::InstanceDied.into_primitive() as i32,
513 ));
514 fruntime::job_default().kill().expect("Failed to kill job");
515 }
516 Event::Controller(Ok(frunner::ComponentControllerRequest::_UnknownMethod {
517 ordinal,
518 method_type,
519 ..
520 })) => {
521 log_error!(ordinal, method_type:?; "Unknown component controller request received.");
522 }
523 Event::Controller(Err(e)) => {
524 log_warn!(e:?; "Container component controller channel encountered an error.");
525 }
526 Event::Completion(result) => {
527 log_info!(result:?; "init process exited.");
528 }
529 }
530
531 if !kernel.is_shutting_down() {
533 kernel.shut_down();
534 }
535 }
536
537 log_debug!("done listening for container-terminating events");
538
539 if !kernel.is_shutting_down() {
541 kernel.shut_down();
542 }
543}
544
545pub async fn create_component_from_stream(
546 mut request_stream: frunner::ComponentRunnerRequestStream,
547 kernel_extra_features: Vec<String>,
548 oom_watermarks_mb: OomWatermarksMb,
549) -> Result<(Container, ContainerServiceConfig), Error> {
550 if let Some(event) = request_stream.try_next().await? {
551 match event {
552 frunner::ComponentRunnerRequest::Start { start_info, controller, .. } => {
553 let request_stream = controller.into_stream();
554 let mut start_info = ContainerStartInfo::new(start_info)?;
555 let (sender, receiver) = oneshot::channel::<TaskResult>();
556 let container = create_container(
557 &mut start_info,
558 &kernel_extra_features,
559 oom_watermarks_mb,
560 sender,
561 )
562 .await
563 .with_source_context(|| {
564 format!("creating container \"{}\"", start_info.program.name)
565 })?;
566 let service_config =
567 ContainerServiceConfig { start_info, request_stream, receiver };
568 return Ok((container, service_config));
569 }
570 frunner::ComponentRunnerRequest::_UnknownMethod { ordinal, .. } => {
571 log_warn!("Unknown ComponentRunner request: {ordinal}");
572 }
573 }
574 }
575 bail!("did not receive Start request");
576}
577
578async fn get_bootargs(device_tree: &Devicetree) -> Result<String, Error> {
579 device_tree
580 .root_node
581 .find("chosen")
582 .and_then(|n| {
583 n.get_property("bootargs").map(|p| {
584 let end =
585 if p.value.last() == Some(&0) { p.value.len() - 1 } else { p.value.len() };
586 match std::str::from_utf8(&p.value[..end]) {
587 Ok(s) => Ok(s.to_owned()),
588 Err(e) => {
589 log_warn!("Bootargs are not valid UTF-8: {e}");
590 Err(anyhow!("Bootargs are not valid UTF-8"))
591 }
592 }
593 })
594 })
595 .context("Couldn't find bootargs")?
596}
597
598async fn get_bootitems() -> Result<std::vec::Vec<u8>, Error> {
599 let items =
600 connect_to_protocol::<fboot::ItemsMarker>().context("Failed to connect to boot items")?;
601
602 let items_response = items
603 .get2(zbi::Type::Devicetree.into(), None)
604 .await
605 .context("FIDL: Failed to get devicetree item")?
606 .map_err(|e| anyhow!("Failed to get devicetree item {:?}", e))?;
607
608 let Some(item) = items_response.last() else {
609 return Err(anyhow!("Failed to get items"));
610 };
611
612 let devicetree_vmo = &item.payload;
613 let bytes = devicetree_vmo
614 .read_to_vec(0, item.length as u64)
615 .context("Failed to read devicetree vmo")?;
616
617 Ok(bytes)
618}
619
620async fn get_serial_number() -> Result<String, Error> {
621 let sysinfo = connect_to_protocol::<fsysinfo::SysInfoMarker>()
622 .context("Failed to connect to fuchsia.sysinfo.SysInfo")?;
623 sysinfo
624 .get_serial_number()
625 .await
626 .context("FIDL: Failed to get serial number")?
627 .map_err(|status| anyhow!("Failed to get serial number: {:?}", status))
628}
629
630const ANDROID_HARDWARE_DDR_BOOTLOADER_FILE: &str = "androidboot.hardware.ddr";
631
632async fn get_bootloader_file_hardware_ddr() -> Result<Option<String>, Error> {
633 let items =
634 connect_to_protocol::<fboot::ItemsMarker>().context("Failed to connect to boot items")?;
635
636 let Some(vmo) = items
637 .get_bootloader_file(ANDROID_HARDWARE_DDR_BOOTLOADER_FILE)
638 .await
639 .context("FIDL: Failed to get bootloader file")?
640 else {
641 return Ok(None);
642 };
643
644 let size = vmo.get_content_size().context("Failed to get bootloader file content size")?;
646 let bytes = vmo.read_to_vec(0, size).context("Failed to read bootloader file vmo")?;
647
648 Ok(Some(String::from_utf8(bytes).context("Bootloader file is not valid UTF-8")?))
649}
650
651async fn create_container(
652 start_info: &mut ContainerStartInfo,
653 kernel_extra_features: &[String],
654 oom_watermarks_mb: OomWatermarksMb,
655 task_complete: oneshot::Sender<TaskResult>,
656) -> Result<Container, Error> {
657 fuchsia_trace::duration!(CATEGORY_STARNIX, NAME_CREATE_CONTAINER);
658 const DEFAULT_INIT: &str = "/container/init";
659
660 let pkg_channel = start_info.container_namespace.get_namespace_channel("/pkg").unwrap();
661 let pkg_dir_proxy = fio::DirectorySynchronousProxy::new(pkg_channel);
662
663 let device_tree: Option<Devicetree> = match get_bootitems().await {
664 Ok(items) => match parse_devicetree(&items) {
665 Ok(device_tree) => Some(device_tree),
666 Err(e) => {
667 log_warn!("Failed to parse devicetree: {e:?}");
668 None
669 }
670 },
671 Err(e) => {
672 log_warn!("Failed to get boot items for devicetree: {e:?}");
673 None
674 }
675 };
676 let (mut features, modules) = parse_features(&start_info, kernel_extra_features)?;
677 features.kernel.oom_watermarks_mb = Some(oom_watermarks_mb);
678
679 log_debug!("Creating container with {features:#?}, {modules:#?}");
680 let mut kernel_cmdline = BString::from(start_info.program.kernel_cmdline.as_bytes());
681 let mut devicetree_bootreason = None;
682
683 let mut bootargs_has_serialno = false;
684 let mut bootargs_has_verifiedbootstate = false;
685 let mut bootargs_has_hardware_ddr = false;
686 if features.android_serialno {
690 if let Some(device_tree) = &device_tree {
691 match get_bootargs(device_tree).await {
692 Ok(args) => {
693 for item in parse_cmdline(&args) {
694 if item.starts_with("androidboot.force_normal_boot") {
695 continue;
697 }
698 if item.starts_with("androidboot.serialno") {
699 bootargs_has_serialno = true;
700 }
701 if item.starts_with("androidboot.verifiedbootstate") {
702 bootargs_has_verifiedbootstate = true;
703 }
704 if item.starts_with("androidboot.hardware.ddr") {
705 bootargs_has_hardware_ddr = true;
706 }
707 if item.starts_with("androidboot.bootreason") && features.android_bootreason
708 {
709 log_info!("Original devicetree bootarg {:?}", item);
713 if let Some((_, v)) = item.split_once('=') {
714 devicetree_bootreason = Some(v.to_string());
715 }
716 continue;
717 }
718 kernel_cmdline.extend(b" ");
719 kernel_cmdline.extend(item.bytes());
720 }
721 }
722 Err(err) => log_warn!("could not get bootargs: {err:?}"),
723 }
724 } else {
725 log_warn!("No devicetree available to get bootargs for android.serialno");
726 }
727
728 if !bootargs_has_serialno {
729 match get_serial_number().await {
730 Ok(serial) => {
731 log_info!("Fell back to sysinfo serial number: {}", serial);
732 kernel_cmdline.extend(b" androidboot.serialno=");
733 kernel_cmdline.extend(serial.bytes());
734 }
735 Err(err) => {
736 log_warn!("Could not get serial number from sysinfo: {err:?}");
737 }
738 }
739 }
740 if !bootargs_has_verifiedbootstate {
741 kernel_cmdline.extend(b" androidboot.verifiedbootstate=orange");
742 }
743 if !bootargs_has_hardware_ddr {
744 match get_bootloader_file_hardware_ddr().await {
745 Ok(Some(ddr)) => {
746 kernel_cmdline.extend(b" androidboot.hardware.ddr=");
747 kernel_cmdline.extend(ddr.bytes());
748 }
749 Ok(None) => {}
750 Err(err) => {
751 log_warn!("Could not get androidboot.hardware.ddr boot item: {err:?}");
752 }
753 }
754 }
755 }
756 if features.android_bootreason {
757 let bootloader_file_bootreason = match get_bootloader_file_bootreason().await {
758 Ok(Some(reason)) => {
759 log_info!("Original bootloader file androidboot.bootreason={:?}", reason);
760 Some(reason)
761 }
762 Ok(None) => None,
763 Err(err) => {
764 log_warn!("Could not get androidboot.bootreason boot item: {err:?}");
765 None
766 }
767 };
768 let android_provided_bootreason = devicetree_bootreason.or(bootloader_file_bootreason);
771 kernel_cmdline.extend(b" androidboot.bootreason=");
772
773 let tmp_channel = start_info.container_namespace.get_namespace_channel("/tmp_lifecycle");
774 let tmp_proxy = match tmp_channel {
775 Ok(channel) => {
776 Some(fio::DirectoryProxy::new(fidl::AsyncChannel::from_channel(channel)))
777 }
778 _ => None,
779 };
780
781 match get_or_init_android_bootreason(tmp_proxy, android_provided_bootreason).await {
782 Ok(reason) => {
783 kernel_cmdline.extend(reason.bytes());
784 }
785 Err(err) => {
786 log_warn!("could not get android bootreason: {err:?}. falling back to 'unknown'");
787 kernel_cmdline.extend(b"unknown");
788 }
789 }
790 }
791 if let Some(params) = modules.extra_cmdline_params()? {
792 kernel_cmdline.extend(b" ");
793 kernel_cmdline.extend(&*params);
794 }
795
796 let mut task_mappings = RoleOverrides::new();
799 for m in &start_info.program.task_role_overrides {
800 task_mappings.add_with_filters(
801 m.process.clone(),
802 m.thread.clone(),
803 m.cgroup.clone(),
804 m.is_leader.as_ref().map(|v| **v),
805 m.min_nice.as_ref().map(|v| **v),
806 m.max_nice.as_ref().map(|v| **v),
807 m.role.clone(),
808 );
809 }
810 let task_mappings = task_mappings.build().context("adding custom task role")?;
811 let scheduler_manager = SchedulerManager::new(task_mappings);
812
813 let crash_reporter = connect_to_protocol::<CrashReporterMarker>().unwrap();
814
815 let node = inspect::component::inspector().root().create_child("container");
816 let kernel_node = node.create_child("kernel");
817 kernel_node.record_int("created_at", zx::MonotonicInstant::get().into_nanos());
818 features.record_inspect(&kernel_node);
819 modules.record_inspect(&kernel_node);
820
821 let security_state = security::kernel_init_security(
822 features.selinux.enabled,
823 features.selinux.options.clone(),
824 features.selinux.exceptions.clone(),
825 &kernel_node,
826 );
827
828 let time_adjustment_proxy = if features.enable_utc_time_adjustment {
831 connect_to_protocol_sync::<AdjustMarker>()
832 .map_err(|e| log_error!("could not connect to fuchsia.time.external/Adjust: {:?}", e))
833 .ok()
834 } else {
835 log_info!("UTC adjustment is forbidden.");
837 None
838 };
839
840 log_info!("final kernel cmdline: {kernel_cmdline:?}");
841 kernel_node.record_string("cmdline", kernel_cmdline.to_str_lossy());
842
843 let kernel = Kernel::new(
844 kernel_cmdline,
845 features.kernel.clone(),
846 std::mem::take(&mut features.system_limits),
847 start_info.container_namespace.try_clone()?,
848 scheduler_manager,
849 Some(crash_reporter),
850 kernel_node,
851 security_state,
852 time_adjustment_proxy,
853 device_tree,
854 )
855 .with_source_context(|| format!("creating Kernel: {}", start_info.program.name))?;
856 let init_pid = kernel.pids.lock().allocate_pid().source_context("allocating init pid")?;
857 let init_tid = init_pid.id;
858 debug_assert_eq!(init_tid, 1);
860
861 let system_task = create_system_task(&kernel).source_context("create system task")?;
862 debug_assert_eq!(system_task.tid.id, 2);
865
866 kernel.kthreads.init(system_task).source_context("initializing kthreads")?;
867 let system_task = kernel.kthreads.system_task();
868
869 init_fs_context(&system_task, &features, start_info, &pkg_dir_proxy)
870 .source_context("initializing FsContext")?;
871
872 kernel.syslog.init(&kernel).source_context("initializing syslog")?;
873
874 kernel.hrtimer_manager.init(system_task).source_context("initializing HrTimer manager")?;
875
876 log_info!("Initializing suspend resume manager.");
877 if let Err(e) = kernel.suspend_resume_manager.init(&system_task) {
878 log_warn!("Suspend/Resume manager initialization failed: ({e:?})");
879 }
880
881 log_info!("Registering devices and filesystems.");
883 init_common_devices(&kernel)?;
884 register_common_file_systems(&kernel);
885
886 register_common_syscalls(&kernel);
887
888 log_info!("Mounting filesystems.");
889 mount_filesystems(&system_task, start_info, &pkg_dir_proxy)
890 .source_context("mounting filesystems")?;
891
892 {
894 log_info!("Running container features.");
895 run_container_features(&kernel, &features)?;
896 log_info!("Initializing container modules.");
897 modules.init_modules(&kernel)?;
898 }
899
900 log_info!("Initializing remote block devices.");
901 init_remote_block_devices(&kernel).source_context("initalizing remote block devices")?;
902
903 let argv = if start_info.program.init.is_empty() {
908 vec![DEFAULT_INIT.to_string()]
909 } else {
910 start_info.program.init.clone()
911 }
912 .iter()
913 .map(|s| to_cstr(s))
914 .collect::<Vec<_>>();
915
916 log_info!("Opening start_info file.");
917 let executable = system_task
918 .open_file_for_exec(
919 starnix_core::vfs::FdNumber::AT_FDCWD,
920 argv[0].as_bytes().into(),
921 OpenFlags::empty(),
922 )
923 .with_source_context(|| format!("opening init: {:?}", argv[0]))?;
924
925 let initial_name = if start_info.program.init.is_empty() {
926 TaskCommand::default()
927 } else {
928 TaskCommand::new(start_info.program.init[0].as_bytes())
929 };
930
931 let rlimits = parse_rlimits(&start_info.program.rlimits)?;
932
933 log_info!("Starting runtime directory.");
935 if let Some(runtime_dir) = start_info.runtime_dir.take() {
936 kernel.kthreads.spawn_future(
937 move || async move { serve_runtime_dir(runtime_dir).await },
938 "serve_runtime_dir",
939 );
940 }
941
942 if let Some(break_on_start) = start_info.break_on_start.take() {
945 log_info!("Waiting for signal from debugger before spawning init process...");
946 if let Err(e) =
947 fuchsia_async::OnSignals::new(break_on_start, zx::Signals::EVENTPAIR_PEER_CLOSED).await
948 {
949 log_warn!(e:%; "Received break_on_start eventpair but couldn't wait for PEER_CLOSED.");
950 }
951 }
952
953 log_info!("Creating init process.");
954 node.record_int("init_started_at", zx::MonotonicInstant::get().into_nanos());
955 let init_task =
956 create_init_process(&kernel, init_pid, initial_name, system_task.fs().fork(), &rlimits)
957 .with_source_context(|| format!("creating init task: {:?}", start_info.program.init))?;
958
959 execute_task_with_prerun_result(
960 init_task,
961 move |init_task| {
962 parse_numbered_handles(init_task, None, &init_task.files()).expect("");
963 init_task.exec(executable, argv[0].clone(), argv.clone(), vec![])
964 },
965 move |result| {
966 log_info!("Finished running init process: {:?}", result);
967 let _ = task_complete.send(result);
968 },
969 )?;
970
971 if !start_info.program.startup_file_path.is_empty() {
972 wait_for_init_file(&start_info.program.startup_file_path, &system_task, init_tid).await?;
973 };
974
975 let memory_attribution_manager = ContainerMemoryAttributionManager::new(
976 Arc::downgrade(&kernel),
977 start_info.component_instance.take().ok_or_else(|| Error::msg("No component instance"))?,
978 );
979
980 Ok(Container {
981 kernel,
982 memory_attribution_manager,
983 _node: node,
984 _thread_bound: Default::default(),
985 })
986}
987
988fn init_fs_context(
989 system_task: &CurrentTask,
990 features: &Features,
991 start_info: &ContainerStartInfo,
992 pkg_dir_proxy: &fio::DirectorySynchronousProxy,
993) -> Result<(), Error> {
994 let kernel = system_task.kernel();
995 let mut mounts_iter =
999 start_info.program.mounts.iter().chain(start_info.config.additional_mounts.iter());
1000 let root = MountAction::new_for_root(
1001 kernel,
1002 pkg_dir_proxy,
1003 mounts_iter.next().ok_or_else(|| anyhow!("Mounts list is empty"))?,
1004 )?;
1005 if root.path != "/" {
1006 anyhow::bail!("First mount in mounts list is not the root");
1007 }
1008
1009 let mut builder = LayeredFsBuilder::new(root.fs);
1010 if features.container {
1011 let component_tmpfs_options = FileSystemOptions {
1014 params: kernel
1015 .features
1016 .ns_mount_options("#component_tmpfs")
1017 .context("#component_tmpfs options")?,
1018 ..Default::default()
1019 };
1020 let component_tmpfs = TmpFs::new_fs_with_options(kernel, component_tmpfs_options)?;
1021
1022 let container_remotefs_options = FileSystemOptions {
1024 source: "data".into(),
1025 params: kernel.features.ns_mount_options("#container").context("#container options")?,
1026 ..Default::default()
1027 };
1028 let container_remotefs = new_remotefs_in_root(
1029 kernel,
1030 pkg_dir_proxy,
1031 container_remotefs_options,
1032 fio::PERM_READABLE | fio::PERM_EXECUTABLE,
1033 )?;
1034
1035 builder.add("/container", container_remotefs);
1036 builder.add("/container/component", component_tmpfs);
1037 }
1038 if features.custom_artifacts {
1039 let mount_options = FileSystemOptions {
1040 params: kernel
1041 .features
1042 .ns_mount_options("#custom_artifacts")
1043 .context("#custom_artifacts options")?,
1044 ..Default::default()
1045 };
1046 let fs = TmpFs::new_fs_with_options(kernel, mount_options)?;
1047 builder.add("/custom_artifacts", fs);
1048 }
1049 if features.test_data {
1050 let mount_options = FileSystemOptions {
1051 params: kernel.features.ns_mount_options("#test_data").context("#test_data options")?,
1052 ..Default::default()
1053 };
1054 let fs = TmpFs::new_fs_with_options(kernel, mount_options)?;
1055 builder.add("/test_data", fs);
1056 }
1057
1058 let (mut root_fs, feature_mounts) = builder.build(kernel);
1059 if features.rootfs_rw {
1060 root_fs = OverlayStack::wrap_fs_in_writable_layer(system_task, root_fs)?;
1061 }
1062
1063 system_task
1064 .fs()
1065 .replace_bootstrap_namespace(system_task, Namespace::new_with_flags(root_fs, root.flags));
1066 feature_mounts(system_task).source_context("mounting feature filesystems")?;
1067
1068 Ok(())
1069}
1070
1071pub(crate) fn parse_rlimits(rlimits: &[String]) -> Result<Vec<(Resource, u64)>, Error> {
1072 let mut res = Vec::new();
1073
1074 for rlimit in rlimits {
1075 let (key, value) =
1076 rlimit.split_once('=').ok_or_else(|| anyhow!("Invalid rlimit: {rlimit}"))?;
1077 let value = value.parse::<u64>().with_context(|| format!("Invalid rlimit: {rlimit}"))?;
1078 let name = key.strip_prefix("RLIMIT_").ok_or_else(|| anyhow!("Unknown rlimit: {key}"))?;
1079 let resource = name.parse::<Resource>().map_err(|_| anyhow!("Unknown rlimit: {key}"))?;
1080 res.push((resource, value));
1081 }
1082
1083 Ok(res)
1084}
1085
1086fn mount_filesystems(
1087 system_task: &CurrentTask,
1088 start_info: &ContainerStartInfo,
1089 pkg_dir_proxy: &fio::DirectorySynchronousProxy,
1090) -> Result<(), Error> {
1091 let mut mounts_iter =
1093 start_info.program.mounts.iter().chain(start_info.config.additional_mounts.iter());
1094 let _ = mounts_iter.next();
1095 for mount_spec in mounts_iter {
1096 let Some(action) = MountAction::from_spec(system_task, pkg_dir_proxy, mount_spec)
1097 .with_source_context(|| format!("creating filesystem from spec: {}", mount_spec))?
1098 else {
1099 continue;
1100 };
1101 let mount_point = system_task
1102 .lookup_path_from_root(action.path.as_ref())
1103 .with_source_context(|| format!("lookup path from root: {}", action.path))?;
1104 mount_point.mount(WhatToMount::Fs(action.fs), action.flags)?;
1105 }
1106 Ok(())
1107}
1108
1109fn init_remote_block_devices(kernel: &Kernel) -> Result<(), Error> {
1110 remote_block_device_init(kernel);
1111 let entries = match std::fs::read_dir("/block") {
1112 Ok(entries) => entries,
1113 Err(e) => {
1114 log_warn!("Failed to read block directory: {}", e);
1115 return Ok(());
1116 }
1117 };
1118 for entry in entries {
1119 let entry = entry?;
1120 let path_buf = entry.path();
1121 let path = path_buf.to_str().ok_or_else(|| anyhow!("Invalid block device path"))?;
1122 let (client_end, server_end) = fidl::endpoints::create_endpoints();
1123 match fdio::service_connect(
1124 &format!("{}/fuchsia.storage.block.Block", path),
1125 server_end.into(),
1126 ) {
1127 Ok(()) => (),
1128 Err(e) => {
1129 log_warn!("Failed to connect to block device at {}: {}", path, e);
1130 continue;
1131 }
1132 }
1133 let name = entry.file_name();
1134 let name_str = name.to_str().unwrap();
1135 kernel
1136 .remote_block_device_registry
1137 .create_remote_block_device(kernel, &name_str, client_end)
1138 .with_source_context(|| format!("creating remote block device: {name_str}"))?;
1139 }
1140 Ok(())
1141}
1142
1143async fn wait_for_init_file(
1144 startup_file_path: &str,
1145 current_task: &CurrentTask,
1146 init_tid: tid_t,
1147) -> Result<(), Error> {
1148 loop {
1150 fasync::Timer::new(fasync::MonotonicDuration::from_millis(100).after_now()).await;
1151
1152 let creds = security::creds_start_internal_operation(current_task);
1153 if let Some(result) = current_task.override_creds(creds, || {
1154 let root = current_task.fs().root();
1155 let mut context = LookupContext::default();
1156
1157 match current_task.lookup_path(&mut context, root, startup_file_path.into()) {
1158 Ok(_) => return Some(Ok(())),
1159 Err(error) if error == ENOENT => {}
1160 Err(error) => return Some(Err(anyhow::Error::from(error))),
1161 };
1162
1163 if current_task.get_task(init_tid).is_err() {
1164 return Some(Err(anyhow!(
1165 "Init task terminated before startup_file_path was ready"
1166 )));
1167 }
1168
1169 None
1170 }) {
1171 return result;
1172 }
1173 }
1174}
1175
1176async fn serve_runtime_dir(runtime_dir: ServerEnd<fio::DirectoryMarker>) {
1177 let mut fs = fuchsia_component::server::ServiceFs::new();
1178 match create_job_id_vmo() {
1179 Ok(vmo) => {
1180 fs.dir("elf").add_vmo_file_at("job_id", vmo);
1181 }
1182 Err(e) => log_error!(e:%; "failed to create vmo with job id for debuggers"),
1183 }
1184 match fs.serve_connection(runtime_dir) {
1185 Ok(_) => {
1186 fs.add_fidl_service(|job_requests: TaskProviderRequestStream| {
1187 fuchsia_async::Task::local(async move {
1188 if let Err(e) = serve_task_provider(job_requests).await {
1189 log_warn!(e:?; "Error serving TaskProvider");
1190 }
1191 })
1192 .detach();
1193 });
1194 fs.collect::<()>().await;
1195 }
1196 Err(e) => log_error!("Couldn't serve runtime directory: {e:?}"),
1197 }
1198}
1199
1200fn create_job_id_vmo() -> Result<zx::Vmo, Error> {
1201 let job_id = fuchsia_runtime::job_default().koid().context("reading own job koid")?;
1202 let job_id_str = job_id.raw_koid().to_string();
1203 let job_id_vmo = zx::Vmo::create(job_id_str.len() as u64).context("creating job id vmo")?;
1204 job_id_vmo.write(job_id_str.as_bytes(), 0).context("write job id to vmo")?;
1205 Ok(job_id_vmo)
1206}
1207
1208async fn serve_task_provider(mut job_requests: TaskProviderRequestStream) -> Result<(), Error> {
1209 while let Some(request) = job_requests.next().await {
1210 match request.context("getting next TaskProvider request")? {
1211 TaskProviderRequest::GetJob { responder } => {
1212 responder
1213 .send(
1214 fuchsia_runtime::job_default()
1215 .duplicate_handle(zx::Rights::SAME_RIGHTS)
1216 .map_err(|s| s.into_raw()),
1217 )
1218 .context("sending job for runtime dir")?;
1219 }
1220 unknown => bail!("Unknown TaskProvider method {unknown:?}"),
1221 }
1222 }
1223 Ok(())
1224}
1225
1226#[cfg(test)]
1227mod test {
1228 use super::{parse_rlimits, wait_for_init_file};
1229
1230 use futures::{SinkExt, StreamExt};
1231 use starnix_core::testing::spawn_kernel_and_run;
1232 use starnix_core::vfs::FdNumber;
1233 use starnix_uapi::CLONE_FS;
1234 use starnix_uapi::file_mode::FileMode;
1235 use starnix_uapi::open_flags::OpenFlags;
1236 use starnix_uapi::resource_limits::Resource;
1237 use starnix_uapi::signals::SIGCHLD;
1238 use starnix_uapi::vfs::ResolveFlags;
1239
1240 #[fuchsia::test]
1241 fn test_parse_rlimits() {
1242 let rlimits = vec![
1243 "RLIMIT_NOFILE=131072".to_string(),
1244 "RLIMIT_RTPRIO=20".to_string(),
1245 "RLIMIT_MEMLOCK=65536".to_string(),
1246 ];
1247 assert_eq!(
1248 parse_rlimits(&rlimits).expect("parse_rlimits"),
1249 vec![(Resource::NOFILE, 131072), (Resource::RTPRIO, 20), (Resource::MEMLOCK, 65536),]
1250 );
1251
1252 assert!(parse_rlimits(&["RLIMIT_NOFILE".to_string()]).is_err());
1253 assert!(parse_rlimits(&["NOFILE=1024".to_string()]).is_err());
1254 assert!(parse_rlimits(&["RLIMIT_UNKNOWN=100".to_string()]).is_err());
1255 assert!(parse_rlimits(&["RLIMIT_NOFILE=not_a_number".to_string()]).is_err());
1256 }
1257
1258 #[fuchsia::test]
1259 async fn test_init_file_already_exists() {
1260 spawn_kernel_and_run(async move |current_task| {
1261 let path = "/path";
1262 current_task
1263 .open_file_at(
1264 FdNumber::AT_FDCWD,
1265 path.into(),
1266 OpenFlags::CREAT,
1267 FileMode::default(),
1268 ResolveFlags::empty(),
1269 )
1270 .expect("Failed to create file");
1271
1272 wait_for_init_file(path, current_task, current_task.get_tid())
1273 .await
1274 .expect("failed to wait for file");
1275 })
1276 .await;
1277 }
1278 #[fuchsia::test]
1279 async fn test_init_file_wait_required() {
1280 spawn_kernel_and_run(async move |current_task| {
1281 let (mut sender, mut receiver) = futures::channel::mpsc::unbounded();
1282
1283 let init_task = current_task.clone_task_for_test(CLONE_FS as u64, Some(SIGCHLD));
1284 let path = "/path";
1285
1286 let test_init_tid = current_task.get_tid();
1287
1288 let wait_fut = async {
1289 sender.send(()).await.expect("failed to send message");
1290 wait_for_init_file(path, &init_task, test_init_tid)
1291 .await
1292 .expect("failed to wait for file");
1293 sender.send(()).await.expect("failed to send message");
1294 };
1295
1296 let create_fut = async {
1297 assert!(receiver.next().await.is_some());
1298 current_task
1299 .open_file_at(
1300 FdNumber::AT_FDCWD,
1301 path.into(),
1302 OpenFlags::CREAT,
1303 FileMode::default(),
1304 ResolveFlags::empty(),
1305 )
1306 .expect("Failed to create file");
1307 assert!(receiver.next().await.is_some());
1308 };
1309
1310 futures::join!(wait_fut, create_fut);
1311 })
1312 .await;
1313 }
1314 #[fuchsia::test]
1315 async fn test_init_exits_before_file_exists() {
1316 spawn_kernel_and_run(async move |current_task| {
1317 let (mut sender, mut receiver) = futures::channel::mpsc::unbounded();
1318
1319 let init_task = current_task.clone_task_for_test(CLONE_FS as u64, Some(SIGCHLD));
1320 const STARTUP_FILE_PATH: &str = "/path";
1321
1322 let test_init_tid = init_task.get_tid();
1323
1324 let wait_fut = async {
1325 sender.send(()).await.expect("failed to send message");
1326 wait_for_init_file(STARTUP_FILE_PATH, current_task, test_init_tid)
1327 .await
1328 .expect_err("Did not detect init exit");
1329 sender.send(()).await.expect("failed to send message");
1330 };
1331
1332 let exit_fut = async {
1333 assert!(receiver.next().await.is_some());
1334 std::mem::drop(init_task);
1335 assert!(receiver.next().await.is_some());
1336 };
1337
1338 futures::join!(wait_fut, exit_fut);
1339 })
1340 .await;
1341 }
1342}