blob: 023c11c0fb311b87bb6e1fc2f6ce57c28fb0fcd3 [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.
#include "garnet/bin/sysmgr/package_updating_loader.h"
#include <fcntl.h>
#include <string>
#include <utility>
#include <lib/fit/function.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include "lib/fidl/cpp/optional.h"
#include "lib/fsl/io/fd.h"
#include "lib/fsl/vmo/file.h"
#include "lib/fxl/files/unique_fd.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/substitute.h"
#include "lib/pkg_url/url_resolver.h"
#include "lib/svc/cpp/services.h"
namespace sysmgr {
PackageUpdatingLoader::PackageUpdatingLoader(
std::unordered_set<std::string> update_dependency_urls,
fuchsia::amber::ControlPtr amber_ctl, async_dispatcher_t* dispatcher)
: update_dependency_urls_(std::move(update_dependency_urls)),
amber_ctl_(std::move(amber_ctl)),
dispatcher_(dispatcher) {}
PackageUpdatingLoader::~PackageUpdatingLoader() = default;
void PackageUpdatingLoader::LoadUrl(fidl::StringPtr url,
LoadUrlCallback callback) {
// The updating loader can only update fuchsia-pkg URLs.
component::FuchsiaPkgUrl fuchsia_url;
bool parsed = false;
if (component::FuchsiaPkgUrl::IsFuchsiaPkgScheme(url)) {
parsed = fuchsia_url.Parse(url);
} else {
parsed =
fuchsia_url.Parse("fuchsia-pkg://fuchsia.com/" + component::GetPathFromURL(url));
}
if (!parsed) {
PackageLoader::LoadUrl(url, callback);
return;
}
auto done_cb = [this, url,
callback = std::move(callback)](std::string error) mutable {
if (!error.empty()) {
FXL_LOG(ERROR) << "Package update encountered unexpected error \""
<< error << "\": " << url;
callback(nullptr);
return;
}
PackageLoader::LoadUrl(url, callback);
};
if (std::find(update_dependency_urls_.begin(), update_dependency_urls_.end(),
url) != std::end(update_dependency_urls_)) {
// Avoid infinite reentry and cycles: Don't attempt to update the amber or
// any dependent package. Contacting the amber service may require starting
// its component or a dependency, which would end up back here.
done_cb("");
return;
}
StartUpdatePackage(std::move(fuchsia_url), std::move(done_cb));
return;
}
void PackageUpdatingLoader::StartUpdatePackage(
const component::FuchsiaPkgUrl url, DoneCallback done_cb) {
auto cb = [this,
done_cb = std::move(done_cb)](zx::channel reply_chan) mutable {
ListenForPackage(std::move(reply_chan), std::move(done_cb));
};
// TODO(CF-???): pass variant here
amber_ctl_->GetUpdateComplete(url.package_name(), "0", nullptr,
std::move(cb));
}
namespace {
constexpr int ZXSIO_DAEMON_ERROR = ZX_USER_SIGNAL_0;
} // namespace
void PackageUpdatingLoader::ListenForPackage(zx::channel reply_chan,
DoneCallback done_cb) {
async::Wait* wait = new async::Wait(
reply_chan.release(),
ZX_CHANNEL_PEER_CLOSED | ZX_CHANNEL_READABLE | ZXSIO_DAEMON_ERROR,
[done_cb = std::move(done_cb)](async_dispatcher_t* dispatcher,
async::Wait* wait, zx_status_t status,
const zx_packet_signal_t* signal) mutable {
WaitForUpdateDone(dispatcher, wait, status, signal, std::move(done_cb));
});
zx_status_t r = wait->Begin(dispatcher_);
if (r != ZX_OK) {
delete wait;
done_cb(fxl::Substitute("Failed to start waiting for package update: $0",
fxl::StringView(zx_status_get_string(r))));
}
}
// static
void PackageUpdatingLoader::WaitForUpdateDone(async_dispatcher_t* dispatcher,
async::Wait* wait,
zx_status_t status,
const zx_packet_signal_t* signal,
DoneCallback done_cb) {
if (status == ZX_OK && (signal->observed & ZXSIO_DAEMON_ERROR)) {
// Daemon signalled an error, wait for its error message.
const zx_handle_t reply_chan = wait->object();
delete wait;
wait = new async::Wait(
reply_chan, ZX_CHANNEL_PEER_CLOSED | ZX_CHANNEL_READABLE,
[done_cb = std::move(done_cb)](
async_dispatcher_t* dispatcher, async::Wait* wait,
zx_status_t status, const zx_packet_signal_t* signal) mutable {
FinishWaitForUpdate(dispatcher, wait, status, signal, true,
std::move(done_cb));
});
zx_status_t r = wait->Begin(dispatcher);
if (r != ZX_OK) {
delete wait;
done_cb(fxl::Substitute("Failed to start waiting for package update: $0",
fxl::StringView(zx_status_get_string(r))));
}
return;
}
FinishWaitForUpdate(dispatcher, wait, status, signal, false,
std::move(done_cb));
}
// static
void PackageUpdatingLoader::FinishWaitForUpdate(
async_dispatcher_t* dispatcher, async::Wait* wait, zx_status_t status,
const zx_packet_signal_t* signal, bool daemon_err, DoneCallback done_cb) {
const zx_handle_t reply_chan = wait->object();
delete wait;
if (status != ZX_OK) {
done_cb(fxl::Substitute("Failed waiting for package update: $0",
fxl::StringView(zx_status_get_string(status))));
return;
}
if (status == ZX_OK && (signal->observed & ZX_CHANNEL_READABLE)) {
// Read response from channel.
uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES];
uint32_t actual_bytes, actual_handles;
zx_status_t r = zx_channel_read(
reply_chan, 0, bytes, /*handles=*/nullptr, ZX_CHANNEL_MAX_MSG_BYTES,
/*num_handles=*/0, &actual_bytes, &actual_handles);
if (r != ZX_OK) {
done_cb(fxl::Substitute("Error reading response from channel: $0",
fxl::StringView(zx_status_get_string(r))));
return;
}
bytes[actual_bytes] = '\0';
if (daemon_err) {
// If the package daemon reported an error (for example, maybe it could
// not access the remote server), log a warning but allow the stale
// package to be loaded.
FXL_VLOG(1) << "Package update failed. Loading package without "
<< "update. Error: " << bytes;
}
done_cb("");
} else if (status == ZX_OK && (signal->observed & ZX_CHANNEL_PEER_CLOSED)) {
done_cb("Update response channel closed unexpectedly.");
} else {
done_cb(fxl::Substitute("Waiting for update failed: $0",
fxl::StringView(zx_status_get_string(status))));
}
}
} // namespace sysmgr