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