207 lines
7.1 KiB
C++
207 lines
7.1 KiB
C++
//
|
|
// Copyright (C) 2016 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#include <xz.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <base/command_line.h>
|
|
#include <base/strings/string_split.h>
|
|
#include <base/strings/stringprintf.h>
|
|
#include <brillo/asynchronous_signal_handler.h>
|
|
#include <brillo/flag_helper.h>
|
|
#include <brillo/message_loops/base_message_loop.h>
|
|
#include <brillo/streams/file_stream.h>
|
|
#include <brillo/streams/stream.h>
|
|
|
|
#include "update_engine/aosp/update_attempter_android.h"
|
|
#include "update_engine/common/boot_control.h"
|
|
#include "update_engine/common/error_code_utils.h"
|
|
#include "update_engine/common/hardware.h"
|
|
#include "update_engine/common/logging.h"
|
|
#include "update_engine/common/prefs.h"
|
|
#include "update_engine/common/subprocess.h"
|
|
#include "update_engine/common/terminator.h"
|
|
#include "update_engine/common/utils.h"
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
using update_engine::UpdateEngineStatus;
|
|
using update_engine::UpdateStatus;
|
|
|
|
namespace chromeos_update_engine {
|
|
namespace {
|
|
|
|
class SideloadDaemonState : public DaemonStateInterface,
|
|
public ServiceObserverInterface {
|
|
public:
|
|
explicit SideloadDaemonState(brillo::StreamPtr status_stream)
|
|
: status_stream_(std::move(status_stream)) {
|
|
// Add this class as the only observer.
|
|
observers_.insert(this);
|
|
}
|
|
~SideloadDaemonState() override = default;
|
|
|
|
// DaemonStateInterface overrides.
|
|
bool StartUpdater() override { return true; }
|
|
void AddObserver(ServiceObserverInterface* observer) override {}
|
|
void RemoveObserver(ServiceObserverInterface* observer) override {}
|
|
const std::set<ServiceObserverInterface*>& service_observers() override {
|
|
return observers_;
|
|
}
|
|
|
|
// ServiceObserverInterface overrides.
|
|
void SendStatusUpdate(
|
|
const UpdateEngineStatus& update_engine_status) override {
|
|
UpdateStatus status = update_engine_status.status;
|
|
double progress = update_engine_status.progress;
|
|
if (status_ != status && (status == UpdateStatus::DOWNLOADING ||
|
|
status == UpdateStatus::FINALIZING)) {
|
|
// Split the progress bar in two parts for the two stages DOWNLOADING and
|
|
// FINALIZING.
|
|
ReportStatus(base::StringPrintf(
|
|
"ui_print Step %d/2", status == UpdateStatus::DOWNLOADING ? 1 : 2));
|
|
ReportStatus(base::StringPrintf("progress 0.5 0"));
|
|
}
|
|
if (status_ != status || fabs(progress - progress_) > 0.005) {
|
|
ReportStatus(base::StringPrintf("set_progress %.lf", progress));
|
|
}
|
|
progress_ = progress;
|
|
status_ = status;
|
|
}
|
|
|
|
void SendPayloadApplicationComplete(ErrorCode error_code) override {
|
|
if (error_code != ErrorCode::kSuccess) {
|
|
ReportStatus(
|
|
base::StringPrintf("ui_print Error applying update: %d (%s)",
|
|
error_code,
|
|
utils::ErrorCodeToString(error_code).c_str()));
|
|
}
|
|
error_code_ = error_code;
|
|
brillo::MessageLoop::current()->BreakLoop();
|
|
}
|
|
|
|
// Getters.
|
|
UpdateStatus status() { return status_; }
|
|
ErrorCode error_code() { return error_code_; }
|
|
|
|
private:
|
|
// Report a status message in the status_stream_, if any. These messages
|
|
// should conform to the specification defined in the Android recovery.
|
|
void ReportStatus(const string& message) {
|
|
if (!status_stream_)
|
|
return;
|
|
string status_line = message + "\n";
|
|
status_stream_->WriteAllBlocking(
|
|
status_line.data(), status_line.size(), nullptr);
|
|
}
|
|
|
|
std::set<ServiceObserverInterface*> observers_;
|
|
brillo::StreamPtr status_stream_;
|
|
|
|
// The last status and error code reported.
|
|
UpdateStatus status_{UpdateStatus::IDLE};
|
|
ErrorCode error_code_{ErrorCode::kSuccess};
|
|
double progress_{-1.};
|
|
};
|
|
|
|
// Apply an update payload directly from the given payload URI.
|
|
bool ApplyUpdatePayload(const string& payload,
|
|
int64_t payload_offset,
|
|
int64_t payload_size,
|
|
const vector<string>& headers,
|
|
int64_t status_fd) {
|
|
brillo::BaseMessageLoop loop;
|
|
loop.SetAsCurrent();
|
|
|
|
// Setup the subprocess handler.
|
|
brillo::AsynchronousSignalHandler handler;
|
|
handler.Init();
|
|
Subprocess subprocess;
|
|
subprocess.Init(&handler);
|
|
|
|
SideloadDaemonState sideload_daemon_state(
|
|
brillo::FileStream::FromFileDescriptor(status_fd, true, nullptr));
|
|
|
|
// During the sideload we don't access the prefs persisted on disk but instead
|
|
// use a temporary memory storage.
|
|
MemoryPrefs prefs;
|
|
|
|
std::unique_ptr<BootControlInterface> boot_control =
|
|
boot_control::CreateBootControl();
|
|
if (!boot_control) {
|
|
LOG(ERROR) << "Error initializing the BootControlInterface.";
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<HardwareInterface> hardware = hardware::CreateHardware();
|
|
if (!hardware) {
|
|
LOG(ERROR) << "Error initializing the HardwareInterface.";
|
|
return false;
|
|
}
|
|
|
|
UpdateAttempterAndroid update_attempter(&sideload_daemon_state,
|
|
&prefs,
|
|
boot_control.get(),
|
|
hardware.get(),
|
|
nullptr);
|
|
update_attempter.Init();
|
|
|
|
TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
|
|
payload, payload_offset, payload_size, headers, nullptr));
|
|
|
|
loop.Run();
|
|
return sideload_daemon_state.status() == UpdateStatus::UPDATED_NEED_REBOOT;
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace chromeos_update_engine
|
|
|
|
int main(int argc, char** argv) {
|
|
DEFINE_string(payload,
|
|
"file:///data/payload.bin",
|
|
"The URI to the update payload to use.");
|
|
DEFINE_int64(
|
|
offset, 0, "The offset in the payload where the CrAU update starts. ");
|
|
DEFINE_int64(size,
|
|
0,
|
|
"The size of the CrAU part of the payload. If 0 is passed, it "
|
|
"will be autodetected.");
|
|
DEFINE_string(headers,
|
|
"",
|
|
"A list of key-value pairs, one element of the list per line.");
|
|
DEFINE_int64(status_fd, -1, "A file descriptor to notify the update status.");
|
|
|
|
chromeos_update_engine::Terminator::Init();
|
|
chromeos_update_engine::SetupLogging(true /* stderr */, false /* file */);
|
|
brillo::FlagHelper::Init(argc, argv, "Update Engine Sideload");
|
|
|
|
LOG(INFO) << "Update Engine Sideloading starting";
|
|
|
|
// xz-embedded requires to initialize its CRC-32 table once on startup.
|
|
xz_crc32_init();
|
|
|
|
vector<string> headers = base::SplitString(
|
|
FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
|
|
|
|
if (!chromeos_update_engine::ApplyUpdatePayload(
|
|
FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|