blob: 53c09752da501479a6c3b7f788ce41f5634b400e [file] [log] [blame]
Darin Petkovf2065b42011-05-17 16:36:27 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Darin Petkova4a8a8c2010-07-15 22:21:12 -07002// 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/omaha_request_params.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <sys/utsname.h>
10
11#include <map>
12#include <string>
Darin Petkova3df55b2010-11-15 13:33:55 -080013#include <vector>
Darin Petkova4a8a8c2010-07-15 22:21:12 -070014
Darin Petkov49d91322010-10-25 16:34:58 -070015#include <base/file_util.h>
Patrick Dubroy7fbbe8a2011-08-01 17:28:22 +020016#include <policy/device_policy.h>
Darin Petkov49d91322010-10-25 16:34:58 -070017
Darin Petkova4a8a8c2010-07-15 22:21:12 -070018#include "update_engine/simple_key_value_store.h"
Jay Srinivasanae4697c2013-03-18 17:08:08 -070019#include "update_engine/system_state.h"
Darin Petkova4a8a8c2010-07-15 22:21:12 -070020#include "update_engine/utils.h"
21
Darin Petkov49d91322010-10-25 16:34:58 -070022#define CALL_MEMBER_FN(object, member) ((object).*(member))
23
Darin Petkova4a8a8c2010-07-15 22:21:12 -070024using std::map;
25using std::string;
Darin Petkova3df55b2010-11-15 13:33:55 -080026using std::vector;
Darin Petkova4a8a8c2010-07-15 22:21:12 -070027
Darin Petkova4a8a8c2010-07-15 22:21:12 -070028namespace chromeos_update_engine {
29
Darin Petkov5a7f5652010-07-22 21:40:09 -070030const char* const OmahaRequestParams::kAppId(
31 "{87efface-864d-49a5-9bb3-4b050a7c227a}");
32const char* const OmahaRequestParams::kOsPlatform("Chrome OS");
33const char* const OmahaRequestParams::kOsVersion("Indy");
Jay Srinivasan55f50c22013-01-10 19:24:35 -080034const char* const kProductionOmahaUrl(
Darin Petkov5a7f5652010-07-22 21:40:09 -070035 "https://tools.google.com/service/update2");
36
Jay Srinivasanae4697c2013-03-18 17:08:08 -070037const char* const OmahaRequestParams::kUpdateChannelKey(
38 "CHROMEOS_RELEASE_TRACK");
39const char* const OmahaRequestParams::kIsPowerwashAllowedKey(
40 "CHROMEOS_IS_POWERWASH_ALLOWED");
Patrick Dubroy7fbbe8a2011-08-01 17:28:22 +020041
Jay Srinivasanae4697c2013-03-18 17:08:08 -070042const char* kChannelsByStability[] = {
43 // This list has to be sorted from least stable to most stable channel.
44 "canary-channel",
45 "dev-channel",
46 "beta-channel",
47 "stable-channel",
48};
Darin Petkov49d91322010-10-25 16:34:58 -070049
Jay Srinivasanae4697c2013-03-18 17:08:08 -070050bool OmahaRequestParams::Init(const std::string& in_app_version,
51 const std::string& in_update_url,
52 bool in_interactive) {
53 InitFromLsbValue();
Darin Petkov10d02dd2011-01-10 14:57:39 -080054 bool stateful_override = !ShouldLockDown();
Jay Srinivasanae4697c2013-03-18 17:08:08 -070055 os_platform_ = OmahaRequestParams::kOsPlatform;
56 os_version_ = OmahaRequestParams::kOsVersion;
57 app_version_ = in_app_version.empty() ?
Darin Petkov10d02dd2011-01-10 14:57:39 -080058 GetLsbValue("CHROMEOS_RELEASE_VERSION", "", NULL, stateful_override) :
59 in_app_version;
Jay Srinivasanae4697c2013-03-18 17:08:08 -070060 os_sp_ = app_version_ + "_" + GetMachineType();
61 os_board_ = GetLsbValue("CHROMEOS_RELEASE_BOARD",
62 "",
63 NULL,
64 stateful_override);
65 app_id_ = GetLsbValue("CHROMEOS_RELEASE_APPID",
66 OmahaRequestParams::kAppId,
67 NULL,
68 stateful_override);
69 board_app_id_ = GetLsbValue("CHROMEOS_BOARD_APPID",
70 app_id_,
71 NULL,
72 stateful_override);
73 app_lang_ = "en-US";
74 hwid_ = utils::GetHardwareClass();
Patrick Dubroy7fbbe8a2011-08-01 17:28:22 +020075
Jay Srinivasanae4697c2013-03-18 17:08:08 -070076 if (current_channel_ == target_channel_) {
77 // deltas are only okay if the /.nodelta file does not exist. if we don't
78 // know (i.e. stat() returns some unexpected error), then err on the side of
79 // caution and say deltas are not okay.
80 struct stat stbuf;
81 delta_okay_ = (stat((root_ + "/.nodelta").c_str(), &stbuf) < 0) &&
82 (errno == ENOENT);
83
Patrick Dubroy7fbbe8a2011-08-01 17:28:22 +020084 } else {
Jay Srinivasanae4697c2013-03-18 17:08:08 -070085 LOG(INFO) << "Disabling deltas as a channel change is pending";
86 // For now, disable delta updates if the current channel is different from
87 // the channel that we're sending to the update server because such updates
88 // are destined to fail -- the current rootfs hash will be different than
89 // the expected hash due to the different channel in /etc/lsb-release.
90 delta_okay_ = false;
Patrick Dubroy7fbbe8a2011-08-01 17:28:22 +020091 }
92
Jay Srinivasanae4697c2013-03-18 17:08:08 -070093 if (in_update_url.empty())
94 update_url_ = GetLsbValue("CHROMEOS_AUSERVER", kProductionOmahaUrl, NULL,
95 stateful_override);
96 else
97 update_url_ = in_update_url;
Gilad Arnoldbbdd4902013-01-10 16:06:30 -080098
99 // Set the interactive flag accordingly.
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700100 interactive_ = in_interactive;
Darin Petkova4a8a8c2010-07-15 22:21:12 -0700101 return true;
102}
103
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700104bool OmahaRequestParams::SetTargetChannel(const std::string& new_target_channel,
105 bool is_powerwash_allowed) {
106 LOG(INFO) << "SetTargetChannel called with " << new_target_channel
107 << ". Is Powerwash Allowed = "
108 << utils::ToString(is_powerwash_allowed);
109
110 // Ignore duplicate calls so we can make the method succeed and be
111 // idempotent so as not to surface unnecessary errors to the UI.
112 if (new_target_channel == target_channel_ &&
113 is_powerwash_allowed == is_powerwash_allowed_) {
114 if (new_target_channel == current_channel_) {
115 // Return true to make such calls no-op and idempotent.
116 LOG(INFO) << "SetTargetChannel: Already on " << current_channel_;
117 return true;
118 }
119
120 LOG(INFO) << "SetTargetChannel: Target channel has already been set";
121 return true;
122 }
123
124 // See if there's a channel change already in progress. If so, don't honor
125 // a new channel change until the existing request is fulfilled.
126 if (current_channel_ != target_channel_) {
127 // Avoid dealing with multiple pending channels as they cause a lot of
128 // edge cases that's not worth adding the complexity for.
129 LOG(ERROR) << "Cannot change to " << new_target_channel
130 << " now as we're currently in " << current_channel_
131 << " and the request to change to " << target_channel_
132 << " is pending";
133 return false;
134 }
135
136 if (current_channel_ == "canary-channel") {
137 // TODO(jaysri): chromium-os:39751: We don't have the UI warnings yet. So,
138 // enable the powerwash-on-changing-to-more-stable-channel behavior for now
139 // only on canary-channel devices.
140 is_powerwash_allowed = true;
141 LOG(INFO) << "Is Powerwash Allowed set to true as we are in canary-channel";
142 } else if (!utils::IsOfficialBuild() &&
143 current_channel_ == "testimage-channel") {
144 // Also, allow test builds to have the powerwash behavior so we can always
145 // test channel changing behavior on them, without having to first get them
146 // on an official channel.
147 is_powerwash_allowed = true;
148 LOG(INFO) << "Is Powerwash Allowed set to true as we are running an "
149 "unofficial build";
150 }
151
152 TEST_AND_RETURN_FALSE(IsValidChannel(new_target_channel));
Darin Petkov49d91322010-10-25 16:34:58 -0700153 FilePath kFile(root_ + utils::kStatefulPartition + "/etc/lsb-release");
154 string file_data;
155 map<string, string> data;
156 if (file_util::ReadFileToString(kFile, &file_data)) {
157 data = simple_key_value_store::ParseString(file_data);
158 }
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700159 data[kUpdateChannelKey] = new_target_channel;
160 data[kIsPowerwashAllowedKey] = is_powerwash_allowed ? "true" : "false";
Darin Petkov49d91322010-10-25 16:34:58 -0700161 file_data = simple_key_value_store::AssembleString(data);
162 TEST_AND_RETURN_FALSE(file_util::CreateDirectory(kFile.DirName()));
163 TEST_AND_RETURN_FALSE(
164 file_util::WriteFile(kFile, file_data.data(), file_data.size()) ==
165 static_cast<int>(file_data.size()));
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700166 target_channel_ = new_target_channel;
167 is_powerwash_allowed_ = is_powerwash_allowed;
Darin Petkov49d91322010-10-25 16:34:58 -0700168 return true;
169}
170
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700171void OmahaRequestParams::SetTargetChannelFromLsbValue() {
172 string target_channel_new_value = GetLsbValue(
173 kUpdateChannelKey,
174 "",
175 &chromeos_update_engine::OmahaRequestParams::IsValidChannel,
176 true); // stateful_override
177
178 if (target_channel_ != target_channel_new_value) {
179 target_channel_ = target_channel_new_value;
180 LOG(INFO) << "Target Channel set to " << target_channel_
181 << " from LSB file";
182 }
Darin Petkov49d91322010-10-25 16:34:58 -0700183}
184
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700185void OmahaRequestParams::SetCurrentChannelFromLsbValue() {
186 string current_channel_new_value = GetLsbValue(
187 kUpdateChannelKey,
188 "",
189 NULL, // No need to validate the read-only rootfs channel.
190 false); // stateful_override is false so we get the current channel.
191
192 if (current_channel_ != current_channel_new_value) {
193 current_channel_ = current_channel_new_value;
194 LOG(INFO) << "Current Channel set to " << current_channel_
195 << " from LSB file in rootfs";
196 }
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900197}
198
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700199void OmahaRequestParams::SetIsPowerwashAllowedFromLsbValue() {
200 string is_powerwash_allowed_str = GetLsbValue(
201 kIsPowerwashAllowedKey,
202 "false",
203 NULL, // no need to validate
204 true); // always get it from stateful, as that's the only place it'll be
205 bool is_powerwash_allowed_new_value = (is_powerwash_allowed_str == "true");
206 if (is_powerwash_allowed_ != is_powerwash_allowed_new_value) {
207 is_powerwash_allowed_ = is_powerwash_allowed_new_value;
208 LOG(INFO) << "Powerwash Allowed set to "
209 << utils::ToString(is_powerwash_allowed_)
210 << " from LSB file in stateful";
211 }
212}
213
214void OmahaRequestParams::InitFromLsbValue() {
215 SetTargetChannelFromLsbValue();
216 SetCurrentChannelFromLsbValue();
217 SetIsPowerwashAllowedFromLsbValue();
218}
219
220string OmahaRequestParams::GetLsbValue(const string& key,
221 const string& default_value,
222 ValueValidator validator,
223 bool stateful_override) const {
Darin Petkova3df55b2010-11-15 13:33:55 -0800224 vector<string> files;
225 if (stateful_override) {
226 files.push_back(string(utils::kStatefulPartition) + "/etc/lsb-release");
227 }
228 files.push_back("/etc/lsb-release");
229 for (vector<string>::const_iterator it = files.begin();
230 it != files.end(); ++it) {
231 // TODO(adlr): make sure files checked are owned as root (and all their
232 // parents are recursively, too).
Darin Petkova4a8a8c2010-07-15 22:21:12 -0700233 string file_data;
Gilad Arnold19a45f02012-07-19 12:36:10 -0700234 if (!utils::ReadFile(root_ + *it, &file_data))
Darin Petkova4a8a8c2010-07-15 22:21:12 -0700235 continue;
236
237 map<string, string> data = simple_key_value_store::ParseString(file_data);
Darin Petkov49d91322010-10-25 16:34:58 -0700238 if (utils::MapContainsKey(data, key)) {
239 const string& value = data[key];
240 if (validator && !CALL_MEMBER_FN(*this, validator)(value)) {
241 continue;
242 }
243 return value;
244 }
Darin Petkova4a8a8c2010-07-15 22:21:12 -0700245 }
246 // not found
247 return default_value;
248}
249
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700250string OmahaRequestParams::GetMachineType() const {
Darin Petkova4a8a8c2010-07-15 22:21:12 -0700251 struct utsname buf;
252 string ret;
253 if (uname(&buf) == 0)
254 ret = buf.machine;
255 return ret;
256}
257
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700258bool OmahaRequestParams::ShouldLockDown() const {
Darin Petkov10d02dd2011-01-10 14:57:39 -0800259 if (force_lock_down_) {
260 return forced_lock_down_;
261 }
262 return utils::IsOfficialBuild() && utils::IsNormalBootMode();
Darin Petkov49d91322010-10-25 16:34:58 -0700263}
264
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700265bool OmahaRequestParams::IsValidChannel(const std::string& channel) const {
266 return GetChannelIndex(channel) >= 0;
Darin Petkov49d91322010-10-25 16:34:58 -0700267}
268
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700269void OmahaRequestParams::set_root(const std::string& root) {
270 root_ = root;
271 InitFromLsbValue();
272}
273
274void OmahaRequestParams::SetLockDown(bool lock) {
Darin Petkov10d02dd2011-01-10 14:57:39 -0800275 force_lock_down_ = true;
276 forced_lock_down_ = lock;
Darin Petkov49d91322010-10-25 16:34:58 -0700277}
278
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700279int OmahaRequestParams::GetChannelIndex(const std::string& channel) const {
280 for (size_t t = 0; t < arraysize(kChannelsByStability); ++t)
281 if (channel == kChannelsByStability[t])
282 return t;
283
284 return -1;
285}
286
287bool OmahaRequestParams::to_more_stable_channel() const {
288 int current_channel_index = GetChannelIndex(current_channel_);
289 int target_channel_index = GetChannelIndex(target_channel_);
290
291 return target_channel_index > current_channel_index;
292}
293
Darin Petkova4a8a8c2010-07-15 22:21:12 -0700294} // namespace chromeos_update_engine