blob: a44fc92d10fee2cc25145c624f01f92d840839b9 [file] [log] [blame]
Jay Srinivasan6f6ea002012-12-14 11:26:28 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/payload_state.h"
6
7#include <base/logging.h>
8#include <base/stringprintf.h>
9
10#include "update_engine/omaha_request_action.h"
11#include "update_engine/prefs.h"
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080012#include "update_engine/utils.h"
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080013
14using std::string;
15
16namespace chromeos_update_engine {
17
18// Returns a string containing that subset of the fields from the OmahaResponse
19// which we're interested in persisting for the purpose of detecting whether
20// we should clear the rest of the payload state when we get a new
21// OmahaResponse.
22static string GetFilteredResponse(const OmahaResponse& response) {
23 string mini_response = StringPrintf("NumURLs = %d\n",
24 response.payload_urls.size());
25
26 for (size_t i = 0; i < response.payload_urls.size(); i++)
27 mini_response += StringPrintf("Url%d = %s\n",
28 i, response.payload_urls[i].c_str());
29
30 mini_response += StringPrintf("Payload Size = %llu\n"
31 "Payload Sha256 Hash = %s\n"
32 "Metadata Size = %llu\n"
33 "Metadata Signature = %s\n",
34 response.size,
35 response.hash.c_str(),
36 response.metadata_size,
37 response.metadata_signature.c_str());
38 return mini_response;
39}
40
41bool PayloadState::Initialize(PrefsInterface* prefs) {
42 CHECK(prefs);
43 prefs_ = prefs;
44 LoadResponse();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080045 LoadPayloadAttemptNumber();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080046 LoadUrlIndex();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080047 LoadUrlFailureCount();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080048 LogPayloadState();
49 return true;
50}
51
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080052void PayloadState::SetResponse(const OmahaResponse& omaha_response) {
53 CHECK(prefs_);
54 num_urls_ = omaha_response.payload_urls.size();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080055 max_failure_count_per_url_ = omaha_response.max_failure_count_per_url;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080056 string new_response = GetFilteredResponse(omaha_response);
57 bool has_response_changed = (response_ != new_response);
58 response_ = new_response;
59 LOG(INFO) << "Stored Response = \n" << response_;
60 prefs_->SetString(kPrefsCurrentResponse, response_);
61
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080062 bool should_reset = false;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080063 if (has_response_changed) {
64 LOG(INFO) << "Resetting all payload state as this is a new response";
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080065 should_reset = true;
66 } else if (url_index_ >= num_urls_) {
67 LOG(INFO) << "Resetting all payload state as the persisted state "
68 << "seems to have been tampered with";
69 should_reset = true;
70 }
71
72 if (should_reset) {
73 SetPayloadAttemptNumber(0);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080074 SetUrlIndex(0);
75 }
76}
77
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080078void PayloadState::DownloadComplete() {
79 LOG(INFO) << "Payload downloaded successfully";
80 IncrementPayloadAttemptNumber();
81}
82
83void PayloadState::DownloadProgress(size_t count) {
84 if (count == 0)
85 return;
86
87 // We've received non-zero bytes from a recent download operation. Since our
88 // URL failure count is meant to penalize a URL only for consecutive
89 // failures, downloading bytes successfully means we should reset the failure
90 // count (as we know at least that the URL is working). In future, we can
91 // design this to be more sophisticated to check for more intelligent failure
92 // patterns, but right now, even 1 byte downloaded will mark the URL to be
93 // good unless it hits 10 (or configured number of) consecutive failures
94 // again.
95
96 if (GetUrlFailureCount() == 0)
97 return;
98
99 LOG(INFO) << "Resetting failure count of Url" << GetUrlIndex()
100 << " to 0 as we received " << count << " bytes successfully";
101 SetUrlFailureCount(0);
102}
103
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800104void PayloadState::UpdateFailed(ActionExitCode error) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800105 ActionExitCode base_error = utils::GetBaseErrorCode(error);
106 LOG(INFO) << "Updating payload state for error code: " << base_error;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800107
108 if (!num_urls_) {
109 // Since we don't persist num_urls_, it's possible that we get an error in
110 // our communication to Omaha before even OmahaRequestAction code had a
111 // chance to call SetResponse (which sets num_urls_). So we should not
112 // advance the url_index_ in such cases.
113 LOG(INFO) << "Ignoring failures until we get a valid Omaha response.";
114 return;
115 }
116
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800117 switch (base_error) {
118 // Errors which are good indicators of a problem with a particular URL or
119 // the protocol used in the URL or entities in the communication channel
120 // (e.g. proxies). We should try the next available URL in the next update
121 // check to quickly recover from these errors.
122 case kActionCodePayloadHashMismatchError:
123 case kActionCodePayloadSizeMismatchError:
124 case kActionCodeDownloadPayloadVerificationError:
125 case kActionCodeDownloadPayloadPubKeyVerificationError:
126 case kActionCodeSignedDeltaPayloadExpectedError:
127 case kActionCodeDownloadInvalidMetadataMagicString:
128 case kActionCodeDownloadSignatureMissingInManifest:
129 case kActionCodeDownloadManifestParseError:
130 case kActionCodeDownloadMetadataSignatureError:
131 case kActionCodeDownloadMetadataSignatureVerificationError:
132 case kActionCodeDownloadMetadataSignatureMismatch:
133 case kActionCodeDownloadOperationHashVerificationError:
134 case kActionCodeDownloadOperationExecutionError:
135 case kActionCodeDownloadOperationHashMismatch:
136 case kActionCodeDownloadInvalidMetadataSize:
137 case kActionCodeDownloadInvalidMetadataSignature:
138 case kActionCodeDownloadOperationHashMissingError:
139 case kActionCodeDownloadMetadataSignatureMissingError:
140 IncrementUrlIndex();
141 break;
142
143 // Errors which seem to be just transient network/communication related
144 // failures and do not indicate any inherent problem with the URL itself.
145 // So, we should keep the current URL but just increment the
146 // failure count to give it more chances. This way, while we maximize our
147 // chances of downloading from the URLs that appear earlier in the response
148 // (because download from a local server URL that appears earlier in a
149 // response is preferable than downloading from the next URL which could be
150 // a internet URL and thus could be more expensive).
151 case kActionCodeError:
152 case kActionCodeDownloadTransferError:
153 case kActionCodeDownloadWriteError:
154 case kActionCodeDownloadStateInitializationError:
155 case kActionCodeOmahaErrorInHTTPResponse: // Aggregate code for HTTP errors.
156 IncrementFailureCount();
157 break;
158
159 // Errors which are not specific to a URL and hence shouldn't result in
160 // the URL being penalized. This can happen in two cases:
161 // 1. We haven't started downloading anything: These errors don't cost us
162 // anything in terms of actual payload bytes, so we should just do the
163 // regular retries at the next update check.
164 // 2. We have successfully downloaded the payload: In this case, the
165 // payload attempt number would have been incremented and would take care
166 // of the back-off at the next update check.
167 // In either case, there's no need to update URL index or failure count.
168 case kActionCodeOmahaRequestError:
169 case kActionCodeOmahaResponseHandlerError:
170 case kActionCodePostinstallRunnerError:
171 case kActionCodeFilesystemCopierError:
172 case kActionCodeInstallDeviceOpenError:
173 case kActionCodeKernelDeviceOpenError:
174 case kActionCodeDownloadNewPartitionInfoError:
175 case kActionCodeNewRootfsVerificationError:
176 case kActionCodeNewKernelVerificationError:
177 case kActionCodePostinstallBootedFromFirmwareB:
178 case kActionCodeOmahaRequestEmptyResponseError:
179 case kActionCodeOmahaRequestXMLParseError:
180 case kActionCodeOmahaResponseInvalid:
181 case kActionCodeOmahaUpdateIgnoredPerPolicy:
182 case kActionCodeOmahaUpdateDeferredPerPolicy:
183 LOG(INFO) << "Not incrementing URL index or failure count for this error";
184 break;
185
186 case kActionCodeSuccess: // success code
187 case kActionCodeSetBootableFlagError: // unused
188 case kActionCodeUmaReportedMax: // not an error code
189 case kActionCodeOmahaRequestHTTPResponseBase: // aggregated already
190 case kActionCodeResumedFlag: // not an error code
191 case kActionCodeBootModeFlag: // not an error code
192 case kActualCodeMask: // not an error code
193 // These shouldn't happen. Enumerating these explicitly here so that we
194 // can let the compiler warn about new error codes that are added to
195 // action_processor.h but not added here.
196 LOG(WARNING) << "Unexpected error code for UpdateFailed";
197 break;
198
199 // Note: Not adding a default here so as to let the compiler warn us of
200 // any new enums that were added in the .h but not listed in this switch.
201 }
202}
203
204void PayloadState::LogPayloadState() {
205 LOG(INFO) << "Current Payload State:\n"
206 << "Current Response = \n" << response_
207 << "\nPayload Attempt Number = " << payload_attempt_number_
208 << "\nCurrent URL Index = " << url_index_
209 << "\nCurrent URL Failure Count = " << url_failure_count_;
210}
211
212void PayloadState::IncrementPayloadAttemptNumber() {
213 LOG(INFO) << "Incrementing the payload attempt number";
214 SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
215
216 // TODO(jaysri): chromium-os:36806: Implement the payload back-off logic
217 // that uses the payload attempt number.
218}
219
220void PayloadState::IncrementUrlIndex() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800221 uint32_t next_url_index = GetUrlIndex() + 1;
222 if (next_url_index < num_urls_) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800223 LOG(INFO) << "Incrementing the URL index for next attempt";
224 SetUrlIndex(next_url_index);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800225 } else {
226 LOG(INFO) << "Resetting the current URL index (" << GetUrlIndex() << ") to "
227 << "0 as we only have " << num_urls_ << " URL(s)";
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800228 SetUrlIndex(0);
229 IncrementPayloadAttemptNumber();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800230 }
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800231}
232
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800233void PayloadState::IncrementFailureCount() {
234 uint32_t next_url_failure_count = GetUrlFailureCount() + 1;
235 if (next_url_failure_count < max_failure_count_per_url_) {
236 LOG(INFO) << "Incrementing the URL failure count";
237 SetUrlFailureCount(next_url_failure_count);
238 } else {
239 LOG(INFO) << "Reached max number of failures for Url" << GetUrlIndex()
240 << ". Trying next available URL";
241 IncrementUrlIndex();
242 }
243}
244
245void PayloadState::LoadResponse() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800246 CHECK(prefs_);
247 string stored_value;
248 if (prefs_->Exists(kPrefsCurrentResponse) &&
249 prefs_->GetString(kPrefsCurrentResponse, &stored_value)) {
250 response_ = stored_value;
251 }
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800252}
253
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800254void PayloadState::LoadPayloadAttemptNumber() {
255 CHECK(prefs_);
256 int64_t stored_value;
257 if (prefs_->Exists(kPrefsPayloadAttemptNumber) &&
258 prefs_->GetInt64(kPrefsPayloadAttemptNumber, &stored_value)) {
259 if (stored_value < 0) {
260 LOG(ERROR) << "Invalid payload attempt number (" << stored_value
261 << ") in persisted state. Defaulting to 0";
262 stored_value = 0;
263 }
264 payload_attempt_number_ = stored_value;
265 }
266}
267
268void PayloadState::SetPayloadAttemptNumber(uint32_t payload_attempt_number) {
269 CHECK(prefs_);
270 payload_attempt_number_ = payload_attempt_number;
271 LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number_;
272 prefs_->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number_);
273}
274
275void PayloadState::LoadUrlIndex() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800276 CHECK(prefs_);
277 int64_t stored_value;
278 if (prefs_->Exists(kPrefsCurrentUrlIndex) &&
279 prefs_->GetInt64(kPrefsCurrentUrlIndex, &stored_value)) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800280 if (stored_value < 0) {
281 LOG(ERROR) << "Invalid URL Index (" << stored_value
282 << ") in persisted state. Defaulting to 0";
283 stored_value = 0;
284 }
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800285 url_index_ = stored_value;
286 }
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800287}
288
289void PayloadState::SetUrlIndex(uint32_t url_index) {
290 CHECK(prefs_);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800291 url_index_ = url_index;
292 LOG(INFO) << "Current URL Index = " << url_index_;
293 prefs_->SetInt64(kPrefsCurrentUrlIndex, url_index_);
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800294
295 // Everytime we set the URL index, we should also reset its failure count.
296 // Otherwise, the URL will be tried only once, instead of
297 // max_failure_count_per_url times in the next round.
298 SetUrlFailureCount(0);
299}
300
301void PayloadState::LoadUrlFailureCount() {
302 CHECK(prefs_);
303 int64_t stored_value;
304 if (prefs_->Exists(kPrefsCurrentUrlFailureCount) &&
305 prefs_->GetInt64(kPrefsCurrentUrlFailureCount, &stored_value)) {
306 if (stored_value < 0) {
307 LOG(ERROR) << "Invalid URL Failure count (" << stored_value
308 << ") in persisted state. Defaulting to 0";
309 stored_value = 0;
310 }
311 url_failure_count_ = stored_value;
312 }
313}
314
315void PayloadState::SetUrlFailureCount(uint32_t url_failure_count) {
316 CHECK(prefs_);
317 url_failure_count_ = url_failure_count;
318 LOG(INFO) << "Current URL (Url" << GetUrlIndex()
319 << ")'s Failure Count = " << url_failure_count_;
320 prefs_->SetInt64(kPrefsCurrentUrlFailureCount, url_failure_count_);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800321}
322
323} // namespace chromeos_update_engine