omaha_request_params.cc revision aea4c1cea20dda7ae7e85fc8924a2d784f70d806
1//
2// Copyright (C) 2011 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 "update_engine/omaha_request_params.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <sys/utsname.h>
22
23#include <map>
24#include <string>
25#include <vector>
26
27#include <base/files/file_util.h>
28#include <base/strings/string_util.h>
29#include <chromeos/key_value_store.h>
30#include <policy/device_policy.h>
31
32#include "update_engine/constants.h"
33#include "update_engine/hardware_interface.h"
34#include "update_engine/system_state.h"
35#include "update_engine/utils.h"
36
37#define CALL_MEMBER_FN(object, member) ((object).*(member))
38
39using std::map;
40using std::string;
41using std::vector;
42
43namespace chromeos_update_engine {
44
45const char kProductionOmahaUrl[] =
46    "https://tools.google.com/service/update2";
47const char kAUTestOmahaUrl[] =
48    "https://omaha.sandbox.google.com/service/update2";
49
50const char OmahaRequestParams::kAppId[] =
51    "{87efface-864d-49a5-9bb3-4b050a7c227a}";
52const char OmahaRequestParams::kOsPlatform[] = "Chrome OS";
53const char OmahaRequestParams::kOsVersion[] = "Indy";
54const char OmahaRequestParams::kUpdateChannelKey[] = "CHROMEOS_RELEASE_TRACK";
55const char OmahaRequestParams::kIsPowerwashAllowedKey[] =
56    "CHROMEOS_IS_POWERWASH_ALLOWED";
57const char OmahaRequestParams::kAutoUpdateServerKey[] = "CHROMEOS_AUSERVER";
58
59const char* kChannelsByStability[] = {
60    // This list has to be sorted from least stable to most stable channel.
61    "canary-channel",
62    "dev-channel",
63    "beta-channel",
64    "stable-channel",
65};
66
67bool OmahaRequestParams::Init(const string& in_app_version,
68                              const string& in_update_url,
69                              bool in_interactive) {
70  LOG(INFO) << "Initializing parameters for this update attempt";
71  InitFromLsbValue();
72  bool stateful_override = !ShouldLockDown();
73  os_platform_ = OmahaRequestParams::kOsPlatform;
74  os_version_ = OmahaRequestParams::kOsVersion;
75  app_version_ = in_app_version.empty() ?
76      GetLsbValue("CHROMEOS_RELEASE_VERSION", "", nullptr, stateful_override) :
77      in_app_version;
78  os_sp_ = app_version_ + "_" + GetMachineType();
79  os_board_ = GetLsbValue("CHROMEOS_RELEASE_BOARD",
80                          "",
81                          nullptr,
82                          stateful_override);
83  string release_app_id = GetLsbValue("CHROMEOS_RELEASE_APPID",
84                                      OmahaRequestParams::kAppId,
85                                      nullptr,
86                                      stateful_override);
87  board_app_id_ = GetLsbValue("CHROMEOS_BOARD_APPID",
88                              release_app_id,
89                              nullptr,
90                              stateful_override);
91  canary_app_id_ = GetLsbValue("CHROMEOS_CANARY_APPID",
92                               release_app_id,
93                               nullptr,
94                               stateful_override);
95  app_lang_ = "en-US";
96  hwid_ = system_state_->hardware()->GetHardwareClass();
97  if (CollectECFWVersions()) {
98    fw_version_ = system_state_->hardware()->GetFirmwareVersion();
99    ec_version_ = system_state_->hardware()->GetECVersion();
100  }
101
102  if (current_channel_ == target_channel_) {
103    // deltas are only okay if the /.nodelta file does not exist.  if we don't
104    // know (i.e. stat() returns some unexpected error), then err on the side of
105    // caution and say deltas are not okay.
106    struct stat stbuf;
107    delta_okay_ = (stat((root_ + "/.nodelta").c_str(), &stbuf) < 0) &&
108                  (errno == ENOENT);
109
110  } else {
111    LOG(INFO) << "Disabling deltas as a channel change is pending";
112    // For now, disable delta updates if the current channel is different from
113    // the channel that we're sending to the update server because such updates
114    // are destined to fail -- the current rootfs hash will be different than
115    // the expected hash due to the different channel in /etc/lsb-release.
116    delta_okay_ = false;
117  }
118
119  if (in_update_url.empty())
120    update_url_ = GetLsbValue(kAutoUpdateServerKey, kProductionOmahaUrl,
121                              nullptr, stateful_override);
122  else
123    update_url_ = in_update_url;
124
125  // Set the interactive flag accordingly.
126  interactive_ = in_interactive;
127  return true;
128}
129
130bool OmahaRequestParams::IsUpdateUrlOfficial() const {
131  return (update_url_ == kAUTestOmahaUrl ||
132          update_url_ == GetLsbValue(kAutoUpdateServerKey, kProductionOmahaUrl,
133                                     nullptr, !ShouldLockDown()));
134}
135
136bool OmahaRequestParams::CollectECFWVersions() const {
137  return base::StartsWithASCII(hwid_, string("SAMS ALEX"), true) ||
138         base::StartsWithASCII(hwid_, string("BUTTERFLY"), true) ||
139         base::StartsWithASCII(hwid_, string("LUMPY"), true) ||
140         base::StartsWithASCII(hwid_, string("PARROT"), true) ||
141         base::StartsWithASCII(hwid_, string("SPRING"), true) ||
142         base::StartsWithASCII(hwid_, string("SNOW"), true);
143}
144
145bool OmahaRequestParams::SetTargetChannel(const string& new_target_channel,
146                                          bool is_powerwash_allowed) {
147  LOG(INFO) << "SetTargetChannel called with " << new_target_channel
148            << ", Is Powerwash Allowed = "
149            << utils::ToString(is_powerwash_allowed)
150            << ". Current channel = " << current_channel_
151            << ", existing target channel = " << target_channel_
152            << ", download channel = " << download_channel_;
153  TEST_AND_RETURN_FALSE(IsValidChannel(new_target_channel));
154  chromeos::KeyValueStore lsb_release;
155  base::FilePath kFile(root_ + kStatefulPartition + "/etc/lsb-release");
156
157  lsb_release.Load(kFile);
158  lsb_release.SetString(kUpdateChannelKey, new_target_channel);
159  lsb_release.SetBoolean(kIsPowerwashAllowedKey, is_powerwash_allowed);
160
161  TEST_AND_RETURN_FALSE(base::CreateDirectory(kFile.DirName()));
162  TEST_AND_RETURN_FALSE(lsb_release.Save(kFile));
163  target_channel_ = new_target_channel;
164  is_powerwash_allowed_ = is_powerwash_allowed;
165  return true;
166}
167
168void OmahaRequestParams::SetTargetChannelFromLsbValue() {
169  string target_channel_new_value = GetLsbValue(
170      kUpdateChannelKey,
171      current_channel_,
172      &chromeos_update_engine::OmahaRequestParams::IsValidChannel,
173      true);  // stateful_override
174
175  if (target_channel_ != target_channel_new_value) {
176    target_channel_ = target_channel_new_value;
177    LOG(INFO) << "Target Channel set to " << target_channel_
178              << " from LSB file";
179  }
180}
181
182void OmahaRequestParams::SetCurrentChannelFromLsbValue() {
183  string current_channel_new_value = GetLsbValue(
184      kUpdateChannelKey,
185      current_channel_,
186      nullptr,  // No need to validate the read-only rootfs channel.
187      false);  // stateful_override is false so we get the current channel.
188
189  if (current_channel_ != current_channel_new_value) {
190    current_channel_ = current_channel_new_value;
191    LOG(INFO) << "Current Channel set to " << current_channel_
192              << " from LSB file in rootfs";
193  }
194}
195
196void OmahaRequestParams::SetIsPowerwashAllowedFromLsbValue() {
197  string is_powerwash_allowed_str = GetLsbValue(
198      kIsPowerwashAllowedKey,
199      "false",
200      nullptr,  // no need to validate
201      true);  // always get it from stateful, as that's the only place it'll be
202  bool is_powerwash_allowed_new_value = (is_powerwash_allowed_str == "true");
203  if (is_powerwash_allowed_ != is_powerwash_allowed_new_value) {
204    is_powerwash_allowed_ = is_powerwash_allowed_new_value;
205    LOG(INFO) << "Powerwash Allowed set to "
206              << utils::ToString(is_powerwash_allowed_)
207              << " from LSB file in stateful";
208  }
209}
210
211void OmahaRequestParams::UpdateDownloadChannel() {
212  if (download_channel_ != target_channel_) {
213    download_channel_ = target_channel_;
214    LOG(INFO) << "Download channel for this attempt = " << download_channel_;
215  }
216}
217
218void OmahaRequestParams::InitFromLsbValue() {
219  SetCurrentChannelFromLsbValue();
220  SetTargetChannelFromLsbValue();
221  SetIsPowerwashAllowedFromLsbValue();
222  UpdateDownloadChannel();
223}
224
225string OmahaRequestParams::GetLsbValue(const string& key,
226                                       const string& default_value,
227                                       ValueValidator validator,
228                                       bool stateful_override) const {
229  vector<string> files;
230  if (stateful_override) {
231    files.push_back(string(kStatefulPartition) + "/etc/lsb-release");
232  }
233  files.push_back("/etc/lsb-release");
234  for (vector<string>::const_iterator it = files.begin();
235       it != files.end(); ++it) {
236    // TODO(adlr): make sure files checked are owned as root (and all their
237    // parents are recursively, too).
238    chromeos::KeyValueStore data;
239    if (!data.Load(base::FilePath(root_ + *it)))
240      continue;
241
242    string value;
243    if (data.GetString(key, &value)) {
244      if (validator && !CALL_MEMBER_FN(*this, validator)(value)) {
245        continue;
246      }
247      return value;
248    }
249  }
250  // not found
251  return default_value;
252}
253
254string OmahaRequestParams::GetMachineType() const {
255  struct utsname buf;
256  string ret;
257  if (uname(&buf) == 0)
258    ret = buf.machine;
259  return ret;
260}
261
262bool OmahaRequestParams::ShouldLockDown() const {
263  if (force_lock_down_) {
264    return forced_lock_down_;
265  }
266  return system_state_->hardware()->IsOfficialBuild() &&
267            system_state_->hardware()->IsNormalBootMode();
268}
269
270bool OmahaRequestParams::IsValidChannel(const string& channel) const {
271  return GetChannelIndex(channel) >= 0;
272}
273
274void OmahaRequestParams::set_root(const string& root) {
275  root_ = root;
276  InitFromLsbValue();
277}
278
279void OmahaRequestParams::SetLockDown(bool lock) {
280  force_lock_down_ = true;
281  forced_lock_down_ = lock;
282}
283
284int OmahaRequestParams::GetChannelIndex(const string& channel) const {
285  for (size_t t = 0; t < arraysize(kChannelsByStability); ++t)
286    if (channel == kChannelsByStability[t])
287      return t;
288
289  return -1;
290}
291
292bool OmahaRequestParams::to_more_stable_channel() const {
293  int current_channel_index = GetChannelIndex(current_channel_);
294  int download_channel_index = GetChannelIndex(download_channel_);
295
296  return download_channel_index > current_channel_index;
297}
298
299string OmahaRequestParams::GetAppId() const {
300  return download_channel_ == "canary-channel" ? canary_app_id_ : board_app_id_;
301}
302
303}  // namespace chromeos_update_engine
304