1 //
2 // Copyright (C) 2016 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <xz.h>
18
19 #include <string>
20 #include <vector>
21
22 #include <base/command_line.h>
23 #include <base/logging.h>
24 #include <base/strings/string_split.h>
25 #include <base/strings/stringprintf.h>
26 #include <brillo/asynchronous_signal_handler.h>
27 #include <brillo/flag_helper.h>
28 #include <brillo/message_loops/base_message_loop.h>
29 #include <brillo/streams/file_stream.h>
30 #include <brillo/streams/stream.h>
31
32 #include "update_engine/common/boot_control.h"
33 #include "update_engine/common/error_code_utils.h"
34 #include "update_engine/common/hardware.h"
35 #include "update_engine/common/prefs.h"
36 #include "update_engine/common/subprocess.h"
37 #include "update_engine/common/terminator.h"
38 #include "update_engine/common/utils.h"
39 #include "update_engine/update_attempter_android.h"
40
41 using std::string;
42 using std::vector;
43 using update_engine::UpdateStatus;
44 using update_engine::UpdateEngineStatus;
45
46 namespace {
47 // The root directory used for temporary files in update_engine_sideload.
48 const char kSideloadRootTempDir[] = "/tmp/update_engine_sideload";
49 } // namespace
50
51 namespace chromeos_update_engine {
52 namespace {
53
SetupLogging()54 void SetupLogging() {
55 string log_file;
56 logging::LoggingSettings log_settings;
57 log_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
58 log_settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
59 log_settings.log_file = nullptr;
60 log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
61
62 logging::InitLogging(log_settings);
63 }
64
65 class SideloadDaemonState : public DaemonStateInterface,
66 public ServiceObserverInterface {
67 public:
SideloadDaemonState(brillo::StreamPtr status_stream)68 explicit SideloadDaemonState(brillo::StreamPtr status_stream)
69 : status_stream_(std::move(status_stream)) {
70 // Add this class as the only observer.
71 observers_.insert(this);
72 }
73 ~SideloadDaemonState() override = default;
74
75 // DaemonStateInterface overrides.
StartUpdater()76 bool StartUpdater() override { return true; }
AddObserver(ServiceObserverInterface * observer)77 void AddObserver(ServiceObserverInterface* observer) override {}
RemoveObserver(ServiceObserverInterface * observer)78 void RemoveObserver(ServiceObserverInterface* observer) override {}
service_observers()79 const std::set<ServiceObserverInterface*>& service_observers() override {
80 return observers_;
81 }
82
83 // ServiceObserverInterface overrides.
SendStatusUpdate(const UpdateEngineStatus & update_engine_status)84 void SendStatusUpdate(
85 const UpdateEngineStatus& update_engine_status) override {
86 UpdateStatus status = update_engine_status.status;
87 double progress = update_engine_status.progress;
88 if (status_ != status && (status == UpdateStatus::DOWNLOADING ||
89 status == UpdateStatus::FINALIZING)) {
90 // Split the progress bar in two parts for the two stages DOWNLOADING and
91 // FINALIZING.
92 ReportStatus(base::StringPrintf(
93 "ui_print Step %d/2", status == UpdateStatus::DOWNLOADING ? 1 : 2));
94 ReportStatus(base::StringPrintf("progress 0.5 0"));
95 }
96 if (status_ != status || fabs(progress - progress_) > 0.005) {
97 ReportStatus(base::StringPrintf("set_progress %.lf", progress));
98 }
99 progress_ = progress;
100 status_ = status;
101 }
102
SendPayloadApplicationComplete(ErrorCode error_code)103 void SendPayloadApplicationComplete(ErrorCode error_code) override {
104 if (error_code != ErrorCode::kSuccess) {
105 ReportStatus(
106 base::StringPrintf("ui_print Error applying update: %d (%s)",
107 error_code,
108 utils::ErrorCodeToString(error_code).c_str()));
109 }
110 error_code_ = error_code;
111 brillo::MessageLoop::current()->BreakLoop();
112 }
113
114 // Getters.
status()115 UpdateStatus status() { return status_; }
error_code()116 ErrorCode error_code() { return error_code_; }
117
118 private:
119 // Report a status message in the status_stream_, if any. These messages
120 // should conform to the specification defined in the Android recovery.
ReportStatus(const string & message)121 void ReportStatus(const string& message) {
122 if (!status_stream_)
123 return;
124 string status_line = message + "\n";
125 status_stream_->WriteAllBlocking(
126 status_line.data(), status_line.size(), nullptr);
127 }
128
129 std::set<ServiceObserverInterface*> observers_;
130 brillo::StreamPtr status_stream_;
131
132 // The last status and error code reported.
133 UpdateStatus status_{UpdateStatus::IDLE};
134 ErrorCode error_code_{ErrorCode::kSuccess};
135 double progress_{-1.};
136 };
137
138 // Apply an update payload directly from the given payload URI.
ApplyUpdatePayload(const string & payload,int64_t payload_offset,int64_t payload_size,const vector<string> & headers,int64_t status_fd)139 bool ApplyUpdatePayload(const string& payload,
140 int64_t payload_offset,
141 int64_t payload_size,
142 const vector<string>& headers,
143 int64_t status_fd) {
144 brillo::BaseMessageLoop loop;
145 loop.SetAsCurrent();
146
147 // Setup the subprocess handler.
148 brillo::AsynchronousSignalHandler handler;
149 handler.Init();
150 Subprocess subprocess;
151 subprocess.Init(&handler);
152
153 SideloadDaemonState sideload_daemon_state(
154 brillo::FileStream::FromFileDescriptor(status_fd, true, nullptr));
155
156 // During the sideload we don't access the prefs persisted on disk but instead
157 // use a temporary memory storage.
158 MemoryPrefs prefs;
159
160 std::unique_ptr<BootControlInterface> boot_control =
161 boot_control::CreateBootControl();
162 if (!boot_control) {
163 LOG(ERROR) << "Error initializing the BootControlInterface.";
164 return false;
165 }
166
167 std::unique_ptr<HardwareInterface> hardware = hardware::CreateHardware();
168 if (!hardware) {
169 LOG(ERROR) << "Error initializing the HardwareInterface.";
170 return false;
171 }
172
173 UpdateAttempterAndroid update_attempter(
174 &sideload_daemon_state, &prefs, boot_control.get(), hardware.get());
175 update_attempter.Init();
176
177 TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload(
178 payload, payload_offset, payload_size, headers, nullptr));
179
180 loop.Run();
181 return sideload_daemon_state.status() == UpdateStatus::UPDATED_NEED_REBOOT;
182 }
183
184 } // namespace
185 } // namespace chromeos_update_engine
186
main(int argc,char ** argv)187 int main(int argc, char** argv) {
188 DEFINE_string(payload,
189 "file:///data/payload.bin",
190 "The URI to the update payload to use.");
191 DEFINE_int64(
192 offset, 0, "The offset in the payload where the CrAU update starts. ");
193 DEFINE_int64(size,
194 0,
195 "The size of the CrAU part of the payload. If 0 is passed, it "
196 "will be autodetected.");
197 DEFINE_string(headers,
198 "",
199 "A list of key-value pairs, one element of the list per line.");
200 DEFINE_int64(status_fd, -1, "A file descriptor to notify the update status.");
201
202 chromeos_update_engine::Terminator::Init();
203 chromeos_update_engine::SetupLogging();
204 brillo::FlagHelper::Init(argc, argv, "Update Engine Sideload");
205
206 LOG(INFO) << "Update Engine Sideloading starting";
207
208 // xz-embedded requires to initialize its CRC-32 table once on startup.
209 xz_crc32_init();
210
211 // When called from recovery, /data is not accessible, so we need to use
212 // /tmp for temporary files.
213 chromeos_update_engine::utils::SetRootTempDir(kSideloadRootTempDir);
214
215 vector<string> headers = base::SplitString(
216 FLAGS_headers, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
217
218 if (!chromeos_update_engine::ApplyUpdatePayload(
219 FLAGS_payload, FLAGS_offset, FLAGS_size, headers, FLAGS_status_fd))
220 return 1;
221
222 return 0;
223 }
224