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