blob: dd9b36489557775468937f3000bba2e332d3d9ab [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@available(added=7)
library fuchsia.component.runner;
using fuchsia.component;
using fuchsia.component.sandbox;
using fuchsia.data;
using fuchsia.diagnostics.types;
using fuchsia.io;
using fuchsia.mem;
using fuchsia.process;
using fuchsia.url;
using zx;
const MAX_NAMESPACE_COUNT uint32 = 32;
const MAX_HANDLE_COUNT uint32 = 128;
/// A protocol used for running components.
///
/// This protocol is implemented by components which provide a runtime
/// environment for other components.
///
/// Note: The component manager is the only intended direct client of this
/// interface.
@discoverable(client="platform")
closed(removed=24) open(added=24) protocol ComponentRunner {
/// Start running a component instance described by `start_info`.
///
/// Component manager binds and uses `controller` to control the
/// lifetime of the newly started component instance.
///
/// Errors are delivered as epitaphs over the `ComponentController`
/// protocol. In the event of an error, the runner must ensure that
/// resources are cleaned up.
strict Start(resource struct {
start_info ComponentStartInfo;
controller server_end:ComponentController;
});
};
/// A single component namespace entry, which describes a namespace mount point
/// (`path`) and the directory backing it (`directory`). This type is usually
/// composed inside a vector. See `ComponentStartInfo.ns` for more details.
type ComponentNamespaceEntry = resource table {
/// The mount point for the directory, including a
/// leading slash. For example: "/pkg", "/svc", or "/config/data".
1: path string:fuchsia.component.MAX_PATH_LENGTH;
/// The directory mounted at the above `path`.
2: directory client_end:fuchsia.io.Directory;
};
/// Parameters for starting a new component instance.
type ComponentStartInfo = resource table {
/// The URL of the component. Called `resolved_url` instead of just `url`
/// for historical reasons.
1: resolved_url fuchsia.url.Url;
/// The component's program declaration.
/// This information originates from `ComponentDecl.program`.
2: program fuchsia.data.Dictionary;
/// The namespace to provide to the component instance.
///
/// A namespace specifies the set of directories that a component instance
/// receives at start-up. Through the namespace directories, a component
/// may access capabilities available to it. The contents of the namespace
/// are mainly determined by the component's `use` declarations but may
/// also contain additional capabilities automatically provided by the
/// framework.
///
/// By convention, a component's namespace typically contains some or all
/// of the following directories:
///
/// - "/svc": A directory containing services that the component requested
/// to use via its "import" declarations.
/// - "/pkg": A directory containing the component's package, including its
/// binaries, libraries, and other assets.
///
/// The mount points specified in each entry must be unique and
/// non-overlapping. For example, [{"/foo", ..}, {"/foo/bar", ..}] is
/// invalid.
3: ns vector<ComponentNamespaceEntry>:MAX_NAMESPACE_COUNT;
/// The directory this component serves.
4: outgoing_dir server_end:fuchsia.io.Directory;
/// The directory served by the runner to present runtime information about
/// the component. The runner must either serve it, or drop it to avoid
/// blocking any consumers indefinitely.
5: runtime_dir server_end:fuchsia.io.Directory;
/// The numbered handles that were passed to the component.
///
/// If the component does not support numbered handles, the runner is expected
/// to close the handles.
6: numbered_handles vector<fuchsia.process.HandleInfo>:MAX_HANDLE_COUNT;
/// Binary representation of the component's configuration.
///
/// # Layout
///
/// The first 2 bytes of the data should be interpreted as an unsigned 16-bit
/// little-endian integer which denotes the number of bytes following it that
/// contain the configuration checksum. After the checksum, all the remaining
/// bytes are a persistent FIDL message of a top-level struct. The struct's
/// fields match the configuration fields of the component's compiled manifest
/// in the same order.
7: encoded_config fuchsia.mem.Data;
/// An eventpair that debuggers can use to defer the launch of the component.
///
/// For example, ELF runners hold off from creating processes in the component
/// until ZX_EVENTPAIR_PEER_CLOSED is signaled on this eventpair. They also
/// ensure that runtime_dir is served before waiting on this eventpair.
/// ELF debuggers can query the runtime_dir to decide whether to attach before
/// they drop the other side of the eventpair, which is sent in the payload of
/// the DebugStarted event in fuchsia.component.events.
8: break_on_start zx.Handle:EVENTPAIR;
/// An opaque token that represents the component instance.
///
/// The `fuchsia.component/Introspector` protocol may be used to get the
/// string moniker of the instance from this token.
///
/// Runners may publish this token as part of diagnostics information, to
/// identify the running component without knowing its moniker.
///
/// The token is invalidated when the component instance is destroyed.
@available(added=HEAD)
9: component_instance zx.Handle:EVENT;
/// A dictionary containing data and handles that the component has escrowed
/// during its previous execution via [`ComponentController.OnEscrow`].
@available(added=HEAD)
10: escrowed_dictionary fuchsia.component.sandbox.DictionaryRef;
};
/// A protocol for binding and controlling the lifetime of a component instance
/// started using `ComponentRunner.Start()`. The component manager is the
/// intended direct client of this protocol.
///
/// When the controlled component instance terminates or becomes inaccessible
/// for any reason, the server closes the connection with an epitaph.
///
/// # Lifecycle
///
/// A component may exist in one of two states: `Started`, or `Stopped`. The
/// component is `Started` from the time `ComponentRunner.Start()` is called
/// until the ComponentRunner closes the ComponentController handle. The
/// component then transitions to `Stopped`.
///
/// Component manager uses ComponentController to terminate a component in two
/// steps:
///
/// 1. Component manager calls `Stop()` to indicate that the ComponentRunner
/// should stop a component's execution and send the `OnStop` event.
/// 2. If after some time the ComponentController is not closed, component
/// manager calls `Kill()` to indicate that the ComponentRunner must halt a
/// component's execution immediately, and then send the `OnStop` event.
/// The component manager may wait some period of time after calling `Kill()`
/// before sending `OnStop`, but makes no guarantees it will wait or for how long.
///
/// Component manager first waits for the ComponentController to close, and
/// then tears down the namespace it hosts for the stopped component. Component
/// manager may call `Kill()` without first having called `Stop()`.
///
/// Before stopping, a component can optionally use `OnEscrow` to store some
/// state in the framework, to receive those state again the next time it is
/// started.
///
/// When the component stops, the runner should send an `OnStop` event
/// instead of just closing the channel, to report the component's termination status
/// (see below) and (optionally) an exit code. Once the runner has sent `OnStop`
/// it is free to close [ComponentRunner]; the component framework will close
/// its end of the channel when it receives this event.
///
/// ## Legacy
///
/// Instead of sending `OnStop`, it is also legal for a runner to close the channel
/// with with an epitaph equal to the termination status, but this is a legacy method
/// for backward compatibility that's no longer recommended.
///
/// # Termination status
///
/// The termination status indicates the component's final disposition in the eyes of
/// the runner.
///
/// Note that termination status is _not_ synonymous with a component's exit code.
/// A component's exit code, which is optional for a runner to report, is an
/// integer that represents the program's own return code. For example, for ELF
/// components, it is the value returned by main(). The termination status is
/// the _runner_'s status code for the component's termination, which may capture
/// failure modes that occur in the context of the runner itself rather than the
/// program.
///
/// The following termination statuses may be sent by the server on error:
///
/// - `ZX_OK`: The component exited successfully, typically because the
/// component was asked to stop or it decided independently to exit.
/// - `INVALID_ARGUMENTS`:
/// * `start_info.resolved_url` is not supported by this
/// runner;
/// * `start_info` contains missing or invalid arguments.
/// - `INSTANCE_CANNOT_START`: The runner could not start the component.
/// For example, a critical part of the program could not be found or
/// loaded, or the referenced binary was invalid for this runner.
/// - `RESOURCE_UNAVAILABLE`: The component could not be launched due to
/// lack of resources.
/// - `INTERNAL`: An unexpected internal runner error was encountered.
/// - `INSTANCE_DIED`: The component instance was started but
/// subsequently terminated with an error.
/// - Other status codes (e.g. `ZX_ERR_PEER_CLOSED`) may indicate a failure
/// of the component runner itself. The component manager may respond to such
/// failures by terminating the component runner's job to ensure system
/// stability.
closed(removed=24) open(added=24) protocol ComponentController {
/// Request to stop the component instance.
///
/// After stopping the component instance, the server should close this
/// connection with an epitaph. After the connection
/// closes, component manager considers this component instance to be
/// Stopped and the component's namespace will be torn down.
strict Stop();
/// Stop this component instance immediately.
///
/// The ComponentRunner must immediately kill the component instance, and
/// then close this connection with an epitaph. After the connection
/// closes, component manager considers this component instance to be
/// Stopped and the component's namespace will be torn down.
///
/// In some cases Kill() may be issued before Stop(), but that is not
/// guaranteed.
strict Kill();
@available(replaced=27)
strict -> OnPublishDiagnostics(resource struct {
payload fuchsia.diagnostics.types.ComponentDiagnostics;
});
/// Event for runners to publish diagnostics to the platform.
///
/// This event signals to the platform that the runner for this
/// component is publishing diagnostics about the runtime of the
/// component. The component manager may optionally expose this data
/// to clients.
@available(added=27)
strict -> OnPublishDiagnostics(resource struct {
payload ComponentDiagnostics;
});
/// Store some of the component's state in the framework, to be redelivered
/// to the component the next time it's started (a practice called
/// "escrowing").
///
/// When the framework receives this event, it will wait until the current
/// execution of the component has finished, then start the component again
/// when the `ZX_CHANNEL_READABLE` signal is observed on `outgoing_dir`.
///
/// Repeated calls will replace the old escrowed value. This is discouraged.
///
/// Handles escrowed via `OnEscrow` are always delivered to the next
/// execution of the component.
@available(added=HEAD)
flexible -> OnEscrow(resource table {
/// Escrow the outgoing directory server endpoint. Whenever the
/// component is started, the framework will return this channel via
/// [`ComponentStartInfo.outgoing_dir`].
1: outgoing_dir server_end:fuchsia.io.Directory;
/// Escrow some user defined state. Whenever the component is started,
/// the framework will return these handles via
/// [`ComponentStartInfo.escrowed_dictionary`].
///
/// The framework will not wait for any signals on these objects.
///
/// ## Example
///
/// Let's say a component needs to escrow an event pair that represents
/// the result of some expensive calculation. It can create a
/// dictionary, put the event pair inside with an appropriate key
/// (e.g. `"my_event_pair"`), then check for that entry on startup.
2: escrowed_dictionary fuchsia.component.sandbox.DictionaryRef;
});
/// Report that the component has stopped, with data about its termination. This will
/// cause the component to make a lifecycle transition to `Stopped`.
///
/// Once the runner has sent `OnStop` it is free to close this [ComponentRunner]; the
/// component framework will close its end of the channel when it receives this event.
///
/// Alternatively, a runner may close the controller channel without this event to signal
/// component stop, but this method is legacy and no longer recommended.
@available(added=HEAD)
flexible -> OnStop(@generated_name("ComponentStopInfo") resource table {
/// The component's termination status, as documented on [ComponentRunner] above.
///
/// The caller should set this field. If it is absent, the framework will assume
/// a value of ZX_OK.
1: termination_status zx.Status;
/// (Optional) The exit code of the component instance.
///
/// Runner implementors may map their runtime specific exit code concept
/// (such as libc exit status) to this field. Or they may choose to
/// leave this blank.
2: exit_code int64;
});
};
// Task associated with a running component.
@available(added=27)
type Task = flexible resource union {
1: job zx.Handle:JOB;
2: process zx.Handle:PROCESS;
3: thread zx.Handle:THREAD;
};
// Contains handles for tasks of the running component. This is emitted by
// runners and handled by component manager to obtain information about the
// process and record diagnostics.
@available(added=27)
type ComponentTasks = resource table {
// The Zircon task for the running component.
// Resource usage for the component is attributed to this task.
1: component_task Task;
// The container task running the component.
// This field is set to the task that runs `component_task`. For
// example, if `component_task` is run a part of a Virtual Machine,
// this task is set so that resources attributed to the parent can be
// subdivided between their children.
//
// This field is used only when a portion of the `component_task`'s resource
// usage is shared with another component. If `component_task` owns exactly
// only those resources used by the component, `parent_task` must be left
// unset.
2: parent_task Task;
};
// Diagnostics information about a component provided by the runner.
@available(added=27)
type ComponentDiagnostics = resource table {
// Tasks associated with the running component.
1: tasks ComponentTasks;
};