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