Skip to main content

starnix_kernel_runner/
container.rs

1// Copyright 2028 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
74/// Manages the memory attribution protocol for a Starnix container.
75struct ContainerMemoryAttributionManager {
76    /// Holds state for the hanging-get attribution protocol.
77    memory_attribution_server: AttributionServerHandle,
78}
79
80impl ContainerMemoryAttributionManager {
81    /// Creates a new [ContainerMemoryAttributionManager] from a Starnix kernel and the moniker
82    /// token of the container component.
83    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    /// Creates a new observer for the attribution information from this container.
96    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
104/// Generates the attribution information for the Starnix kernel ELF component. The attribution
105/// information for the container is handled by the container component, not the kernel
106/// component itself, even if both are hosted within the same kernel process.
107fn attribution_info_for_kernel(
108    kernel: &Kernel,
109    component_instance: &zx::Event,
110) -> Vec<fattribution::AttributionUpdate> {
111    // Start the server to handle the memory attribution requests for the container, and provide
112    // a handle to get detailed attribution. We start a new task as each incoming connection is
113    // independent.
114    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        // This part is created for accounting. It holds the resource used for starnix
125        // kernel operation. It neither has sub-principals, nor publishes attribution,
126        // hence it does not need to be tied to a provider server end.
127        detailed_attribution: None,
128        ..Default::default()
129    };
130
131    let starnix_kernel_attribution = fattribution::UpdatedPrincipal {
132        identifier: starnix_kernel_id, // Recipient.
133        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, // Attribute all the range.
137                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    /// Configuration specified by the component's `program` block.
175    pub program: ContainerProgram,
176
177    pub config: ContainerStructuredConfig,
178
179    /// The outgoing directory of the container, used to serve protocols on behalf of the container.
180    /// For example, the starnix_kernel serves a component runner in the containers' outgoing
181    /// directory.
182    outgoing_dir: Option<zx::Channel>,
183
184    /// Mapping of top-level namespace entries to an associated channel.
185    /// For example, "/svc" to the respective channel.
186    pub container_namespace: ContainerNamespace,
187
188    /// The runtime directory of the container, used to provide CF introspection.
189    runtime_dir: Option<ServerEnd<fio::DirectoryMarker>>,
190
191    /// An eventpair that debuggers can use to defer the launch of the container.
192    break_on_start: Option<zx::EventPair>,
193
194    /// Component moniker token for the container component. This token is used in various protocols
195    /// to uniquely identify a component.
196    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    /// The name of this container.
259    name: String,
260
261    /// The command line for the initial process for this container.
262    init: Vec<String>,
263
264    /// The command line for the kernel.
265    #[serde(default)]
266    kernel_cmdline: String,
267
268    /// The specifications for the file system mounts for this container.
269    #[serde(default)]
270    mounts: Vec<String>,
271
272    /// The features enabled for this container.
273    #[serde(default)]
274    pub features: Vec<String>,
275
276    /// The modules enabled for this container.
277    #[serde(default)]
278    pub modules: Vec<String>,
279
280    /// The resource limits to apply to this container.
281    #[serde(default)]
282    rlimits: Vec<String>,
283
284    /// The path that the container will wait until exists before considering itself to have started.
285    #[serde(default)]
286    startup_file_path: String,
287
288    /// The default seclabel that is applied to components that are instantiated in this container.
289    ///
290    /// Components can override this by setting the `seclabel` field in their program block.
291    #[serde(default)]
292    pub default_seclabel: Option<String>,
293
294    /// The default uid that is applied to components that are instantiated in this container.
295    ///
296    /// Components can override this by setting the `uid` field in their program block.
297    #[serde(default = "default_uid")]
298    pub default_uid: runner::serde::StoreAsString<u32>,
299
300    /// The default mount options to use when mounting directories from a component's namespace.
301    ///
302    /// Each string is expected to follow the format: "<namespace_path>:<mount_options>".
303    pub default_ns_mount_options: Option<Vec<String>>,
304
305    /// Specifies role names to use for "realtime" tasks based on their process & thread names.
306    ///
307    /// Zircon's scheduler doesn't support configuring tasks to always preempt non-"realtime"
308    /// tasks without specifying a constant bandwidth profile. These profiles specify the period and
309    /// expected runtime of a "realtime" task, bounding the amount of work it is allowed to perform
310    /// at an elevated "realtime" priority.
311    ///
312    /// Because constant bandwidth profiles require workload-specific tuning, we can't uniformly
313    /// apply a single profile for all "realtime" tasks. Instead, this container configuration
314    /// allows us to specify different constant bandwidth profiles for different workloads.
315    #[serde(default)]
316    task_role_overrides: Vec<TaskSchedulerMapping>,
317}
318
319/// Specifies a role override for a class of tasks whose process and thread names match provided
320/// patterns.
321#[derive(Default, Deserialize)]
322struct TaskSchedulerMapping {
323    /// The role name to use for tasks matching the provided patterns.
324    role: String,
325    /// A regular expression that will be matched against the process' command.
326    process: String,
327    /// A regular expression that will be matched against the thread's command.
328    thread: String,
329    /// An optional regular expression that will be matched against the task's cgroup path.
330    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
347// Creates a CString from a String. Calling this with an invalid CString will panic.
348fn 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    /// The `Kernel` object that is associated with the container.
361    pub kernel: Arc<Kernel>,
362
363    memory_attribution_manager: ContainerMemoryAttributionManager,
364
365    /// Inspect node holding information about the state of the container.
366    _node: inspect::Node,
367
368    /// Until negative trait bound are implemented, using `*mut u8` to prevent transferring
369    /// Container across threads.
370    _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            // Add `ComponentRunner` to the exposed services of the container, and then serve the
384            // outgoing directory.
385            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            // Expose the root of the container's filesystem.
394            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
457/// The services that are exposed in the container component's outgoing directory.
458enum 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        // We treat any event in the stream as an invitation to shut down.
513        if !kernel.is_shutting_down() {
514            kernel.shut_down();
515        }
516    }
517
518    log_debug!("done listening for container-terminating events");
519
520    // In case the stream ended without an event, shut down the kernel here.
521    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    // `fuchsia.boot.Items` sets ZX_PROP_VMO_CONTENT_SIZE to the file's content size.
620    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    // TODO(https://fxbug.dev/526770691): Newer versions of Android replace the 'androidboot.*'
660    // kernel parameters with 'bootconfig'. We should expose this to the container to avoid having
661    // to manage 'androidboot.*' parameters here.
662    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                            // TODO(https://fxbug.dev/424152964): Support force_normal_boot.
669                            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                            // androidboot.bootreason is sourced from the Fuchsia reboot reason.
683                            // It is still useful to log it from userspace to learn what the
684                            // possible values are.
685                            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        // A device only provides one of these in practice; prefer the legacy devicetree value so
742        // existing devices are unaffected.
743        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    // Check whether we actually have access to a role manager by trying to set our own
770    // thread's role.
771    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    // `config.enable_utc_time_adjustment` is set through config capability
794    // `fuchsia.time.config.WritableUTCTime`.
795    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        // See the comment above. UTC adjustment is a per-product setting.
801        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    // Lots of software assumes that the pid for the init process is 1.
827    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    // The system task gives pid 2. This value is less critical than giving
832    // pid 1 to init, but this value matches what is supposed to happen.
833    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    // Register common devices and add them in sysfs and devtmpfs.
850    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    // Run all common features and modules that were specified in the .cml.
861    {
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    // If there is an init binary path, run it, optionally waiting for the
872    // startup_file_path to be created. The task struct is still used
873    // to initialize the system up until this point, regardless of whether
874    // or not there is an actual init to be run.
875    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    // Serve the runtime directory.
902    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    // At this point the runtime environment has been prepared but nothing is actually running yet.
911    // Pause here if a debugger needs time to attach to the job.
912    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    // The mounts are applied in the order listed. Mounting will fail if the designated mount
964    // point doesn't exist in a previous mount. The root must be first so other mounts can be
965    // applied on top of it.
966    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        // /container/component will be a tmpfs where component using the starnix kernel will have their
980        // package mounted.
981        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        // /container will mount the container pkg
991        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    // Skip the first mount, that was used to create the root filesystem.
1055    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    // TODO(https://fxbug.dev/42178400): Use inotify machinery to wait for the file.
1109    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}