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