blob: d7d77702e4852dd8905b44358ea53adec949f06b [file] [log] [blame]
// Copyright 2020 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.
#include <fidl/fuchsia.device.manager/cpp/wire.h>
#include <fidl/fuchsia.hardware.power.statecontrol/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <fidl/fuchsia.power.broker/cpp/wire.h>
#include <fidl/fuchsia.power.system/cpp/wire.h>
#include <fidl/fuchsia.process.lifecycle/cpp/wire.h>
#include <fidl/fuchsia.sys2/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fit/function.h>
#include <zircon/errors.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <chrono>
#include <optional>
#include <thread>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <fbl/string_printf.h>
#include "src/storage/lib/vfs/cpp/managed_vfs.h"
#include "src/storage/lib/vfs/cpp/pseudo_dir.h"
#include "src/storage/lib/vfs/cpp/service.h"
#include "src/sys/lib/stdout-to-debuglog/cpp/stdout-to-debuglog.h"
namespace fio = fuchsia_io;
namespace statecontrol_fidl = fuchsia_hardware_power_statecontrol;
namespace sys2_fidl = fuchsia_sys2;
namespace broker_fidl = fuchsia_power_broker;
namespace system_fidl = fuchsia_power_system;
// The amount of time that the shim will spend trying to connect to
// power_manager before giving up.
// TODO(https://fxbug.dev/42131944): increase this timeout
const zx::duration SERVICE_CONNECTION_TIMEOUT = zx::sec(2);
// The amount of time that the shim will spend waiting for a manually trigger
// system shutdown to finish before forcefully restarting the system.
const std::chrono::duration MANUAL_SYSTEM_SHUTDOWN_TIMEOUT = std::chrono::minutes(60);
// The valid power levels for the shutdown_control power element.
enum class ShutdownControlLevel : uint8_t {
// The shutdown_control power element is not active.
// System suspension is allowed.
kInactive = 0u,
// The shutdown_control power element is active.
// System suspension is not allowed.
kActive = 1u
};
class SystemStateTransitionServer final
: public fidl::WireServer<fuchsia_device_manager::SystemStateTransition> {
public:
SystemStateTransitionServer() = default;
// fuchsia.device.manager/SystemStateTransition APIs.
void GetTerminationSystemState(GetTerminationSystemStateCompleter::Sync& completer) override;
void GetMexecZbis(GetMexecZbisCompleter::Sync& completer) override;
void set_system_power_state(fuchsia_device_manager::SystemPowerState system_power_state) {
fbl::AutoLock al(&lock_);
system_power_state_ = system_power_state;
}
void set_mexec_kernel_zbi(zx::vmo kernel_zbi) {
fbl::AutoLock al(&lock_);
mexec_kernel_zbi_ = std::move(kernel_zbi);
}
void set_mexec_data_zbi(zx::vmo data_zbi) {
fbl::AutoLock al(&lock_);
mexec_data_zbi_ = std::move(data_zbi);
}
private:
fbl::Mutex lock_;
fuchsia_device_manager::SystemPowerState system_power_state_ __TA_GUARDED(&lock_) =
fuchsia_device_manager::SystemPowerState::kFullyOn;
zx::vmo mexec_kernel_zbi_ __TA_GUARDED(&lock_);
zx::vmo mexec_data_zbi_ __TA_GUARDED(&lock_);
};
class StateControlAdminServer final : public fidl::WireServer<statecontrol_fidl::Admin> {
public:
StateControlAdminServer() : loop_((&kAsyncLoopConfigNoAttachToCurrentThread)) {
loop_.StartThread("SystemStateTransitionLoop");
// Set up a power element that keeps the system awake during reboots and shutdowns.
// The new power element, shutdown_control, has an active dependency on
// SAG's WakeHandling power element when its power level is
// ShutdownControlLevel::kActive.
// Start by getting a dependency token to system activity governor's WakeHandling power element.
zx::result activity_governor = component::Connect<system_fidl::ActivityGovernor>();
if (activity_governor.is_error()) {
fprintf(stderr, "[shutdown-shim]: error connecting to system_activity_governor: %s\n",
activity_governor.status_string());
return;
}
fidl::WireSyncClient activity_governor_client{std::move(activity_governor.value())};
auto get_resp = activity_governor_client->GetPowerElements();
if (get_resp.status() != ZX_OK) {
fprintf(stderr, "[shutdown-shim]: error getting activity governor power elements: %s\n",
get_resp.status_string());
return;
}
if (!get_resp.value().has_wake_handling()) {
fprintf(stderr, "[shutdown-shim]: wake_handling power element not available\n");
return;
}
auto wake_handling = std::move(get_resp.value().wake_handling());
if (!wake_handling.has_active_dependency_token()) {
fprintf(stderr, "[shutdown-shim]: wake_handling dependency token not available\n");
return;
}
// Next, create the new power element with power-broker.
zx::result topology = component::Connect<broker_fidl::Topology>();
if (topology.is_error()) {
fprintf(stderr, "[shutdown-shim]: error connecting to power_broker: %s\n",
topology.status_string());
return;
}
fidl::WireSyncClient topology_client{std::move(topology.value())};
// Use the WakeHandling dependency token to set up an active dependency
// between shutdown-shim's new power element and system-activity-governor's
// WakeHandling power element.
auto requires_token = std::move(wake_handling.active_dependency_token());
auto wake_handling_dep = broker_fidl::wire::LevelDependency{
.dependency_type = broker_fidl::wire::DependencyType::kActive,
.dependent_level = static_cast<uint8_t>(ShutdownControlLevel::kActive),
.requires_token = std::move(requires_token),
.requires_level = static_cast<uint8_t>(system_fidl::wire::WakeHandlingLevel::kActive),
};
std::vector<broker_fidl::wire::LevelDependency> dependencies;
dependencies.push_back(std::move(wake_handling_dep));
auto lessor_endpoints = fidl::CreateEndpoints<broker_fidl::Lessor>();
if (!lessor_endpoints.is_ok()) {
fprintf(stderr, "[shutdown-shim]: error creating Lessor endpoints: %s\n",
lessor_endpoints.status_string());
return;
}
std::vector<uint8_t> valid_levels{
static_cast<uint8_t>(ShutdownControlLevel::kInactive),
static_cast<uint8_t>(ShutdownControlLevel::kActive),
};
fidl::Arena arena;
auto shutdown_control_schema =
broker_fidl::wire::ElementSchema::Builder(arena)
.element_name(fidl::StringView::FromExternal("shutdown_control"))
.valid_levels(fidl::VectorView<uint8_t>::FromExternal(valid_levels))
.initial_current_level(static_cast<uint8_t>(ShutdownControlLevel::kInactive))
.dependencies(
fidl::VectorView<broker_fidl::wire::LevelDependency>::FromExternal(dependencies))
.lessor_channel(std::move(lessor_endpoints->server))
.Build();
auto resp = topology_client->AddElement(std::move(shutdown_control_schema));
if (resp.status() != ZX_OK) {
fprintf(stderr, "[shutdown-shim]: error requesting shutdown_control power element: %s\n",
resp.status_string());
return;
}
if (resp.value().is_error()) {
fprintf(stderr, "[shutdown-shim]: error creating shutdown_control power element: %u\n",
static_cast<uint32_t>(resp.value().error_value()));
return;
}
element_control_channel_client_end_ = std::move(resp.value().value()->element_control_channel);
lessor_client_ = fidl::WireSyncClient{std::move(lessor_endpoints->client)};
}
zx_status_t ExportServices(fbl::RefPtr<fs::PseudoDir>& svc_dir, async_dispatcher* dispatcher);
// fuchsia.hardware.power.statecontrol/Admin APIs.
void PowerFullyOn(PowerFullyOnCompleter::Sync& completer) override;
void Reboot(RebootRequestView request, RebootCompleter::Sync& completer) override;
void RebootToBootloader(RebootToBootloaderCompleter::Sync& completer) override;
void RebootToRecovery(RebootToRecoveryCompleter::Sync& completer) override;
void Poweroff(PoweroffCompleter::Sync& completer) override;
void Mexec(MexecRequestView request, MexecCompleter::Sync& completer) override;
void SuspendToRam(SuspendToRamCompleter::Sync& completer) override;
private:
std::optional<fidl::WireSyncClient<broker_fidl::LeaseControl>> AcquireShutdownControlLease()
const;
SystemStateTransitionServer system_state_transition_server_;
async::Loop loop_;
fidl::ClientEnd<broker_fidl::ElementControl> element_control_channel_client_end_;
fidl::WireSyncClient<broker_fidl::Lessor> lessor_client_;
};
// Opens a service node, failing if the provider of the service does not respond
// to messages within SERVICE_CONNECTION_TIMEOUT.
//
// This is accomplished by opening the service node, writing an invalid message
// to the channel, and observing PEER_CLOSED within the timeout. This is testing
// that something is responding to open requests for this service, as opposed to
// the intended provider for this service being stuck on component resolution
// indefinitely, which causes connection attempts to the component to never
// succeed nor fail. By observing a PEER_CLOSED, we can ensure that the service
// provider received our message and threw it out (or the provider doesn't
// exist). Upon receiving the PEER_CLOSED, we then open a new connection and
// save it in `local`.
//
// This is protecting against packaged components being stuck in resolution for
// forever, which happens if pkgfs never starts up (this always happens on
// bringup). Once a component is able to be resolved, then all new service
// connections will either succeed or fail rather quickly.
template <typename Protocol>
zx::result<fidl::ClientEnd<Protocol>> connect_to_protocol_with_timeout(
const char* name = fidl::DiscoverableProtocolDefaultPath<Protocol>) {
zx::result channel = component::Connect<Protocol>(name);
if (channel.is_error()) {
return channel.take_error();
}
// We want to use the zx_channel_call syscall directly here, because there's
// no way to set the timeout field on the call using the FIDL bindings.
char garbage_data[6] = {0, 1, 2, 3, 4, 5};
zx_channel_call_args_t call = {
// Bytes to send in the channel call
.wr_bytes = garbage_data,
// Handles to send in the channel call
.wr_handles = nullptr,
// Buffer to write received bytes into from the channel call
.rd_bytes = nullptr,
// Buffer to write received handles into from the channel call
.rd_handles = nullptr,
// Number of bytes to send
.wr_num_bytes = 6,
// Number of bytes we can receive
.rd_num_bytes = 0,
// Number of handles we can receive
.rd_num_handles = 0,
};
uint32_t actual_bytes, actual_handles;
switch (zx_status_t status =
channel.value().channel().call(0, zx::deadline_after(SERVICE_CONNECTION_TIMEOUT),
&call, &actual_bytes, &actual_handles);
status) {
case ZX_ERR_TIMED_OUT:
fprintf(stderr, "[shutdown-shim]: timed out connecting to %s\n", name);
return zx::error(status);
case ZX_ERR_PEER_CLOSED:
return component::Connect<Protocol>(name);
default:
fprintf(stderr, "[shutdown-shim]: unexpected response from %s: %s\n", name,
zx_status_get_string(status));
return zx::error(status);
}
}
// Connect to fuchsia.sys2.SystemController and initiate a system shutdown. If
// everything goes well, this function shouldn't return until shutdown is
// complete.
zx_status_t initiate_component_shutdown() {
zx::result local = component::Connect<sys2_fidl::SystemController>();
if (local.is_error()) {
fprintf(stderr, "[shutdown-shim]: error connecting to component_manager: %s\n",
local.status_string());
return local.error_value();
}
fidl::WireSyncClient system_controller_client{std::move(local.value())};
printf("[shutdown-shim]: calling system_controller_client.Shutdown()\n");
auto resp = system_controller_client->Shutdown();
printf("[shutdown-shim]: status was returned: %s\n", resp.status_string());
return resp.status();
}
// Sleeps for MANUAL_SYSTEM_SHUTDOWN_TIMEOUT, and then exits the process
void shutdown_timer() {
std::this_thread::sleep_for(MANUAL_SYSTEM_SHUTDOWN_TIMEOUT);
// We shouldn't still be running at this point
exit(1);
}
// Manually drive a shutdown by setting state as driver_manager's termination
// behavior and then instructing component_manager to perform an orderly
// shutdown of components. If the orderly shutdown takes too long the shim will
// exit with a non-zero exit code, killing the root job.
void drive_shutdown_manually(fuchsia_device_manager::SystemPowerState state) {
fprintf(stderr, "[shutdown-shim]: driving shutdown manually\n");
// Start a new thread that makes us exit uncleanly after a timeout. This will
// guarantee that shutdown doesn't take longer than
// MANUAL_SYSTEM_SHUTDOWN_TIMEOUT, because we're marked as critical to the
// root job and us exiting will bring down userspace and cause a reboot.
std::thread(shutdown_timer).detach();
zx_status_t status = initiate_component_shutdown();
if (status != ZX_OK) {
fprintf(
stderr,
"[shutdown-shim]: error initiating component shutdown, system shutdown impossible: %s\n",
zx_status_get_string(status));
// Recovery from this state is impossible. Exit with a non-zero exit code,
// so our critical marking causes the system to forcefully restart.
exit(1);
}
fprintf(stderr, "[shutdown-shim]: manual shutdown successfully initiated\n");
}
zx_status_t send_command(fidl::WireSyncClient<statecontrol_fidl::Admin> statecontrol_client,
fuchsia_device_manager::SystemPowerState fallback_state,
const statecontrol_fidl::wire::RebootReason* reboot_reason = nullptr,
StateControlAdminServer::MexecRequestView* mexec_request = nullptr) {
switch (fallback_state) {
case fuchsia_device_manager::SystemPowerState::kReboot: {
if (reboot_reason == nullptr) {
fprintf(stderr, "[shutdown-shim]: internal error, bad pointer to reason for reboot\n");
return ZX_ERR_INTERNAL;
}
auto resp = statecontrol_client->Reboot(*reboot_reason);
if (resp.status() != ZX_OK) {
return ZX_ERR_UNAVAILABLE;
}
if (resp->is_error()) {
return resp->error_value();
}
return ZX_OK;
} break;
case fuchsia_device_manager::SystemPowerState::kRebootKernelInitiated: {
auto resp = statecontrol_client->Reboot(*reboot_reason);
if (resp.status() != ZX_OK) {
return ZX_ERR_UNAVAILABLE;
}
if (resp.value().is_error()) {
return resp.value().error_value();
}
return ZX_OK;
} break;
case fuchsia_device_manager::SystemPowerState::kRebootBootloader: {
auto resp = statecontrol_client->RebootToBootloader();
if (resp.status() != ZX_OK) {
return ZX_ERR_UNAVAILABLE;
}
if (resp->is_error()) {
return resp->error_value();
}
return ZX_OK;
} break;
case fuchsia_device_manager::SystemPowerState::kRebootRecovery: {
auto resp = statecontrol_client->RebootToRecovery();
if (resp.status() != ZX_OK) {
return ZX_ERR_UNAVAILABLE;
}
if (resp->is_error()) {
return resp->error_value();
}
return ZX_OK;
} break;
case fuchsia_device_manager::SystemPowerState::kPoweroff: {
auto resp = statecontrol_client->Poweroff();
if (resp.status() != ZX_OK) {
return ZX_ERR_UNAVAILABLE;
}
if (resp->is_error()) {
return resp->error_value();
}
return ZX_OK;
} break;
case fuchsia_device_manager::SystemPowerState::kMexec: {
if (mexec_request == nullptr) {
fprintf(stderr, "[shutdown-shim]: internal error, bad pointer to reason for mexec\n");
return ZX_ERR_INTERNAL;
}
auto resp = statecontrol_client->Mexec(std::move((*mexec_request)->kernel_zbi),
std::move((*mexec_request)->data_zbi));
if (resp.status() != ZX_OK) {
return ZX_ERR_UNAVAILABLE;
}
if (resp->is_error()) {
return resp->error_value();
}
return ZX_OK;
} break;
case fuchsia_device_manager::SystemPowerState::kSuspendRam: {
auto resp = statecontrol_client->SuspendToRam();
if (resp.status() != ZX_OK) {
return ZX_ERR_UNAVAILABLE;
}
if (resp->is_error()) {
return resp->error_value();
}
return ZX_OK;
} break;
default:
return ZX_ERR_INTERNAL;
}
}
// Connects to power_manager and passes a SyncClient to the given function. The
// function is expected to return an error if there was a transport-related
// issue talking to power_manager, in which case this program will talk to
// driver_manager and component_manager to drive shutdown manually.
zx_status_t forward_command(fuchsia_device_manager::SystemPowerState fallback_state,
const statecontrol_fidl::wire::RebootReason* reboot_reason = nullptr) {
zx::result local = connect_to_protocol_with_timeout<statecontrol_fidl::Admin>();
if (local.is_ok()) {
fprintf(stderr, "[shutdown-shim]: forwarding command %d\n",
static_cast<uint8_t>(fallback_state));
zx_status_t status =
send_command(fidl::WireSyncClient(std::move(local.value())), fallback_state, reboot_reason);
if (status != ZX_ERR_UNAVAILABLE && status != ZX_ERR_NOT_SUPPORTED) {
return status;
}
// Power manager may decide not to support suspend. We should respect that and not attempt to
// suspend manually.
if (fallback_state == fuchsia_device_manager::SystemPowerState::kSuspendRam) {
return status;
}
}
fprintf(stderr, "[shutdown-shim]: failed to forward command to power_manager: %s\n",
local.status_string());
drive_shutdown_manually(fallback_state);
// We should block on fuchsia.sys.SystemController forever on this thread, if
// it returns something has gone wrong.
fprintf(stderr, "[shutdown-shim]: we shouldn't still be running, crashing the system\n");
exit(1);
}
void StateControlAdminServer::PowerFullyOn(PowerFullyOnCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void StateControlAdminServer::Reboot(RebootRequestView request, RebootCompleter::Sync& completer) {
auto reboot_control_lease = AcquireShutdownControlLease();
fuchsia_device_manager::SystemPowerState target_state =
fuchsia_device_manager::SystemPowerState::kReboot;
if (request->reason == statecontrol_fidl::wire::RebootReason::kOutOfMemory) {
target_state = fuchsia_device_manager::SystemPowerState::kRebootKernelInitiated;
}
system_state_transition_server_.set_system_power_state(target_state);
zx_status_t status = forward_command(target_state, &request->reason);
if (status == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(status);
}
}
void StateControlAdminServer::RebootToBootloader(RebootToBootloaderCompleter::Sync& completer) {
auto reboot_control_lease = AcquireShutdownControlLease();
system_state_transition_server_.set_system_power_state(
fuchsia_device_manager::SystemPowerState::kRebootBootloader);
zx_status_t status = forward_command(fuchsia_device_manager::SystemPowerState::kRebootBootloader);
if (status == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(status);
}
}
void StateControlAdminServer::RebootToRecovery(RebootToRecoveryCompleter::Sync& completer) {
auto reboot_control_lease = AcquireShutdownControlLease();
system_state_transition_server_.set_system_power_state(
fuchsia_device_manager::SystemPowerState::kRebootRecovery);
zx_status_t status = forward_command(fuchsia_device_manager::SystemPowerState::kRebootRecovery);
if (status == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(status);
}
}
void StateControlAdminServer::Poweroff(PoweroffCompleter::Sync& completer) {
auto reboot_control_lease = AcquireShutdownControlLease();
system_state_transition_server_.set_system_power_state(
fuchsia_device_manager::SystemPowerState::kPoweroff);
zx_status_t status = forward_command(fuchsia_device_manager::SystemPowerState::kPoweroff);
if (status == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(status);
}
}
void StateControlAdminServer::Mexec(MexecRequestView request, MexecCompleter::Sync& completer) {
auto reboot_control_lease = AcquireShutdownControlLease();
// Duplicate the VMOs now, as forwarding the mexec request to power-manager
// will consume them.
zx::vmo kernel_zbi, data_zbi;
if (zx_status_t status = request->kernel_zbi.duplicate(ZX_RIGHT_SAME_RIGHTS, &kernel_zbi);
status != ZX_OK) {
completer.ReplyError(status);
return;
}
if (zx_status_t status = request->data_zbi.duplicate(ZX_RIGHT_SAME_RIGHTS, &data_zbi);
status != ZX_OK) {
completer.ReplyError(status);
return;
}
system_state_transition_server_.set_system_power_state(
fuchsia_device_manager::SystemPowerState::kMexec);
system_state_transition_server_.set_mexec_kernel_zbi(std::move(kernel_zbi));
system_state_transition_server_.set_mexec_data_zbi(std::move(data_zbi));
zx::result local = connect_to_protocol_with_timeout<statecontrol_fidl::Admin>();
if (local.is_ok()) {
fprintf(stderr, "[shutdown-shim]: trying to forward Mexec command\n");
zx_status_t status =
send_command(fidl::WireSyncClient(std::move(local.value())),
fuchsia_device_manager::SystemPowerState::kMexec, nullptr, &request);
if (status == ZX_OK) {
completer.ReplySuccess();
return;
}
if (status != ZX_ERR_UNAVAILABLE && status != ZX_ERR_NOT_SUPPORTED) {
completer.ReplyError(status);
return;
}
// Else, fallback logic.
}
fprintf(stderr, "[shutdown-shim]: failed to forward command to power_manager: %s\n",
local.status_string());
drive_shutdown_manually(fuchsia_device_manager::SystemPowerState::kMexec);
// We should block on fuchsia.sys.SystemController forever on this thread, if
// it returns something has gone wrong.
fprintf(stderr, "[shutdown-shim]: we shouldn't still be running, crashing the system\n");
exit(1);
}
void StateControlAdminServer::SuspendToRam(SuspendToRamCompleter::Sync& completer) {
system_state_transition_server_.set_system_power_state(
fuchsia_device_manager::SystemPowerState::kSuspendRam);
zx_status_t status = forward_command(fuchsia_device_manager::SystemPowerState::kSuspendRam);
if (status == ZX_OK) {
completer.ReplySuccess();
} else {
completer.ReplyError(status);
}
}
std::optional<fidl::WireSyncClient<broker_fidl::LeaseControl>>
StateControlAdminServer::AcquireShutdownControlLease() const {
if (!lessor_client_) {
fprintf(
stderr,
"[shutdown-shim]: no lessor client available, skipping acquiring shutdown_control lease.\n");
return std::nullopt;
}
auto resp = lessor_client_->Lease(static_cast<uint8_t>(ShutdownControlLevel::kActive));
if (resp.status() != ZX_OK) {
fprintf(stderr, "[shutdown-shim]: failed to request lease: %s\n", resp.status_string());
// At this point, shutdown-shim has communicated with system-activity-governor and power-broker,
// but the state of the system is inconsistent with that. Force reboot to recover.
fprintf(stderr, "[shutdown-shim]: system is in an unexpected state, rebooting to recover\n");
exit(1);
}
if (resp.value().is_error()) {
fprintf(stderr, "[shutdown-shim]: failed to acquire lease: %d\n",
static_cast<uint32_t>(resp.value().error_value()));
// At this point, shutdown-shim has communicated with system-activity-governor and power-broker,
// but the state of the system is inconsistent with that. Force reboot to recover.
fprintf(stderr, "[shutdown-shim]: system is in an unexpected state, rebooting to recover\n");
exit(1);
}
auto lease_control_client = fidl::WireSyncClient{std::move(resp.value().value()->lease_control)};
auto status = broker_fidl::wire::LeaseStatus::kUnknown;
while (status != broker_fidl::wire::LeaseStatus::kSatisfied) {
auto status_resp = lease_control_client->WatchStatus(status);
if (status_resp.status() != ZX_OK) {
fprintf(stderr, "[shutdown-shim]: failed to watch lease: %s\n", resp.status_string());
// At this point, shutdown-shim has communicated with system-activity-governor and
// power-broker, but the state of the system is inconsistent with that.
// Force reboot to recover.
fprintf(stderr, "[shutdown-shim]: system is in an unexpected state, rebooting to recover\n");
exit(1);
}
status = status_resp.value().status;
}
fprintf(stderr, "[shutdown-shim]: acquired shutdown_control lease\n");
return std::optional(std::move(lease_control_client));
}
void SystemStateTransitionServer::GetTerminationSystemState(
GetTerminationSystemStateCompleter::Sync& completer) {
fbl::AutoLock al(&lock_);
completer.Reply(system_power_state_);
}
void SystemStateTransitionServer::GetMexecZbis(GetMexecZbisCompleter::Sync& completer) {
fbl::AutoLock al(&lock_);
if (system_power_state_ != fuchsia_device_manager::SystemPowerState::kMexec) {
return completer.ReplyError(ZX_ERR_BAD_STATE);
}
completer.ReplySuccess(std::move(mexec_kernel_zbi_), std::move(mexec_data_zbi_));
}
zx_status_t StateControlAdminServer::ExportServices(fbl::RefPtr<fs::PseudoDir>& svc_dir,
async_dispatcher* dispatcher) {
zx_status_t status =
svc_dir->AddEntry(fidl::DiscoverableProtocolName<statecontrol_fidl::Admin>,
fbl::MakeRefCounted<fs::Service>(
// `fuchsia.hardware.power.statecontrol.Admin| must run on a separate
// thread from `fuchsia.device.manager.SystemStateTransition` as the
// latter has to serve requests while the former makes blocking calls.
// Notice the different dispatcher specified here vs below.
[dispatcher = loop_.dispatcher(),
this](fidl::ServerEnd<statecontrol_fidl::Admin> server_end) mutable {
fidl::BindServer(dispatcher, std::move(server_end), this);
return ZX_OK;
}));
if (status != ZX_OK) {
return status;
}
return svc_dir->AddEntry(
fidl::DiscoverableProtocolName<fuchsia_device_manager::SystemStateTransition>,
fbl::MakeRefCounted<fs::Service>(
// See above comment about how this must run on a separate thread from the other service.
[dispatcher, this](
fidl::ServerEnd<fuchsia_device_manager::SystemStateTransition> server_end) mutable {
fidl::BindServer(dispatcher, std::move(server_end), &system_state_transition_server_);
return ZX_OK;
}));
}
int main() {
zx_status_t status = StdoutToDebuglog::Init();
if (status != ZX_OK) {
return status;
}
printf("[shutdown-shim]: started\n");
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
fs::ManagedVfs outgoing_vfs((loop.dispatcher()));
auto outgoing_dir = fbl::MakeRefCounted<fs::PseudoDir>();
auto svc_dir = fbl::MakeRefCounted<fs::PseudoDir>();
StateControlAdminServer state_control_server;
status = state_control_server.ExportServices(svc_dir, loop.dispatcher());
if (status != ZX_OK) {
fprintf(stderr, "[shutdown-shim]: ExportServices failed with %s\n",
zx_status_get_string(status));
return status;
}
outgoing_dir->AddEntry("svc", std::move(svc_dir));
outgoing_vfs.ServeDirectory(
outgoing_dir,
fidl::ServerEnd<fio::Directory>{zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST))});
loop.Run();
fprintf(stderr, "[shutdown-shim]: exited unexpectedly\n");
return 1;
}