blob: 3455a5262910c4a5e9fe5787d2e4fb4cfaf6795c [file] [log] [blame]
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Darin Petkov5a7f5652010-07-22 21:40:09 -07005#include <string>
6
Ben Chan46bf5c82013-06-24 11:17:41 -07007#include <dbus/dbus.h>
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -07008#include <gflags/gflags.h>
9#include <glib.h>
10
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070011#include "update_engine/marshal.glibmarshal.h"
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070012#include "update_engine/dbus_constants.h"
13#include "update_engine/subprocess.h"
14#include "update_engine/utils.h"
15
16extern "C" {
17#include "update_engine/update_engine.dbusclient.h"
18}
19
20using chromeos_update_engine::kUpdateEngineServiceName;
21using chromeos_update_engine::kUpdateEngineServicePath;
22using chromeos_update_engine::kUpdateEngineServiceInterface;
Darin Petkova0b9e772011-10-06 05:05:56 -070023using chromeos_update_engine::utils::GetAndFreeGError;
Darin Petkov5a7f5652010-07-22 21:40:09 -070024using std::string;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070025
Darin Petkov296889c2010-07-23 16:20:54 -070026DEFINE_string(app_version, "", "Force the current app version.");
Jay Srinivasanae4697c2013-03-18 17:08:08 -070027DEFINE_string(channel, "",
28 "Set the target channel. The device will be powerwashed if the target "
29 "channel is more stable than the current channel.");
Chris Sosad317e402013-06-12 13:47:09 -070030DEFINE_bool(check_for_update, false, "Initiate check for updates.");
31DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
32DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
33DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
34DEFINE_bool(rollback, false, "Perform a rollback to the previous partition.");
35DEFINE_bool(show_channel, false, "Show the current and target channels.");
36DEFINE_bool(powerwash, true, "When performing a rollback, do a powerwash.");
37DEFINE_bool(status, false, "Print the status to stdout.");
Darin Petkov58529db2010-08-13 09:19:47 -070038DEFINE_bool(update, false, "Forces an update and waits for its completion. "
39 "Exit status is 0 if the update succeeded, and 1 otherwise.");
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070040DEFINE_bool(watch_for_updates, false,
41 "Listen for status updates and print them to the screen.");
Alex Deymof4867c42013-06-28 14:41:39 -070042DEFINE_bool(show_update_over_cellular, false,
43 "Show the current setting for updates over cellular networks.");
44DEFINE_string(update_over_cellular, "",
45 "Enables (\"yes\") or disables (\"no\") the updates over "
46 "cellular networks.");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070047
48namespace {
49
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070050bool GetProxy(DBusGProxy** out_proxy) {
51 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070052 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070053 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070054 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070055 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070056
57 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Richard Barnetted7936062013-01-18 13:38:51 -080058 if (bus == NULL) {
59 LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error);
60 exit(1);
61 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070062 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070063 if (i > 0) {
64 LOG(INFO) << "Retrying to get dbus proxy. Try "
65 << (i + 1) << "/" << kTries;
Gilad Arnold8e3f1262013-01-08 14:59:54 -080066 g_usleep(kRetrySeconds * G_USEC_PER_SEC);
Darin Petkova0b9e772011-10-06 05:05:56 -070067 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070068 proxy = dbus_g_proxy_new_for_name_owner(bus,
69 kUpdateEngineServiceName,
70 kUpdateEngineServicePath,
71 kUpdateEngineServiceInterface,
72 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070073 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
74 << kUpdateEngineServiceName << ": "
75 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070076 }
Richard Barnetted7936062013-01-18 13:38:51 -080077 if (proxy == NULL) {
78 LOG(ERROR) << "Giving up -- unable to get dbus proxy for "
79 << kUpdateEngineServiceName;
80 exit(1);
81 }
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070082 *out_proxy = proxy;
83 return true;
84}
85
86static void StatusUpdateSignalHandler(DBusGProxy* proxy,
87 int64_t last_checked_time,
88 double progress,
89 gchar* current_operation,
90 gchar* new_version,
91 int64_t new_size,
92 void* user_data) {
93 LOG(INFO) << "Got status update:";
94 LOG(INFO) << " last_checked_time: " << last_checked_time;
95 LOG(INFO) << " progress: " << progress;
96 LOG(INFO) << " current_operation: " << current_operation;
97 LOG(INFO) << " new_version: " << new_version;
98 LOG(INFO) << " new_size: " << new_size;
99}
100
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700101bool ResetStatus() {
102 DBusGProxy* proxy;
103 GError* error = NULL;
104
105 CHECK(GetProxy(&proxy));
106
107 gboolean rc =
108 org_chromium_UpdateEngineInterface_reset_status(proxy, &error);
109 return rc;
110}
111
112
Darin Petkov58529db2010-08-13 09:19:47 -0700113// If |op| is non-NULL, sets it to the current operation string or an
114// empty string if unable to obtain the current status.
115bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700116 DBusGProxy* proxy;
117 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700118
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700119 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700120
121 gint64 last_checked_time = 0;
122 gdouble progress = 0.0;
123 char* current_op = NULL;
124 char* new_version = NULL;
125 gint64 new_size = 0;
126
127 gboolean rc = org_chromium_UpdateEngineInterface_get_status(
128 proxy,
129 &last_checked_time,
130 &progress,
131 &current_op,
132 &new_version,
133 &new_size,
134 &error);
135 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700136 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700137 }
138 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
139 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
140 last_checked_time,
141 progress,
142 current_op,
143 new_version,
144 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700145 if (op) {
146 *op = current_op ? current_op : "";
147 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700148 return true;
149}
150
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700151// Should never return.
152void WatchForUpdates() {
153 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700154
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700155 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700156
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700157 // Register marshaller
158 dbus_g_object_register_marshaller(
159 update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
160 G_TYPE_NONE,
161 G_TYPE_INT64,
162 G_TYPE_DOUBLE,
163 G_TYPE_STRING,
164 G_TYPE_STRING,
165 G_TYPE_INT64,
166 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700167
168 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700169 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700170 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700171 G_TYPE_INT64,
172 G_TYPE_DOUBLE,
173 G_TYPE_STRING,
174 G_TYPE_STRING,
175 G_TYPE_INT64,
176 G_TYPE_INVALID);
177 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
178 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700179 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700180 G_CALLBACK(StatusUpdateSignalHandler),
181 NULL,
182 NULL);
183 g_main_loop_run(loop);
184 g_main_loop_unref(loop);
185}
186
Chris Sosad317e402013-06-12 13:47:09 -0700187bool Rollback(bool rollback) {
188 DBusGProxy* proxy;
189 GError* error = NULL;
190
191 CHECK(GetProxy(&proxy));
192
193 gboolean rc =
194 org_chromium_UpdateEngineInterface_attempt_rollback(proxy,
195 rollback,
196 &error);
197 CHECK_EQ(rc, TRUE) << "Error with rollback request: "
198 << GetAndFreeGError(&error);
199 return true;
200}
201
Darin Petkov58529db2010-08-13 09:19:47 -0700202bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700203 DBusGProxy* proxy;
204 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700205
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700206 CHECK(GetProxy(&proxy));
207
208 gboolean rc =
Darin Petkov5a7f5652010-07-22 21:40:09 -0700209 org_chromium_UpdateEngineInterface_attempt_update(proxy,
210 app_version.c_str(),
211 omaha_url.c_str(),
212 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700213 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700214 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700215 return true;
216}
217
Darin Petkov296889c2010-07-23 16:20:54 -0700218bool RebootIfNeeded() {
219 DBusGProxy* proxy;
220 GError* error = NULL;
221
222 CHECK(GetProxy(&proxy));
223
224 gboolean rc =
225 org_chromium_UpdateEngineInterface_reboot_if_needed(proxy, &error);
226 // Reboot error code doesn't necessarily mean that a reboot
227 // failed. For example, D-Bus may be shutdown before we receive the
228 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700229 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700230 return true;
231}
232
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700233void SetTargetChannel(const string& target_channel) {
Darin Petkov8daa3242010-10-25 13:28:47 -0700234 DBusGProxy* proxy;
235 GError* error = NULL;
236
237 CHECK(GetProxy(&proxy));
238
239 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700240 org_chromium_UpdateEngineInterface_set_channel(proxy,
241 target_channel.c_str(),
242 true, // OK to Powerwash
243 &error);
244 CHECK_EQ(rc, true) << "Error setting the channel: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700245 << GetAndFreeGError(&error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700246 LOG(INFO) << "Channel permanently set to: " << target_channel;
Darin Petkov8daa3242010-10-25 13:28:47 -0700247}
248
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700249string GetChannel(bool get_current_channel) {
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900250 DBusGProxy* proxy;
251 GError* error = NULL;
252
253 CHECK(GetProxy(&proxy));
254
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700255 char* channel = NULL;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900256 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700257 org_chromium_UpdateEngineInterface_get_channel(proxy,
258 get_current_channel,
259 &channel,
260 &error);
261 CHECK_EQ(rc, true) << "Error getting the channel: "
262 << GetAndFreeGError(&error);
263 string output = channel;
264 g_free(channel);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900265 return output;
266}
267
Alex Deymof4867c42013-06-28 14:41:39 -0700268bool SetUpdateOverCellularPermission(gboolean allowed) {
269 DBusGProxy* proxy;
270 GError* error = NULL;
271
272 CHECK(GetProxy(&proxy));
273
274 gboolean rc =
275 org_chromium_UpdateEngineInterface_set_update_over_cellular_permission(
276 proxy,
277 allowed,
278 &error);
279 CHECK_EQ(rc, true) << "Error setting the update over cellular setting: "
280 << GetAndFreeGError(&error);
281 return true;
282}
283
284bool GetUpdateOverCellularPermission() {
285 DBusGProxy* proxy;
286 GError* error = NULL;
287
288 CHECK(GetProxy(&proxy));
289
290 gboolean allowed;
291 gboolean rc =
292 org_chromium_UpdateEngineInterface_get_update_over_cellular_permission(
293 proxy,
294 &allowed,
295 &error);
296 CHECK_EQ(rc, true) << "Error getting the update over cellular setting: "
297 << GetAndFreeGError(&error);
298 return allowed;
299}
300
Darin Petkov58529db2010-08-13 09:19:47 -0700301static gboolean CompleteUpdateSource(gpointer data) {
302 string current_op;
303 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
304 LOG(ERROR) << "Update failed.";
305 exit(1);
306 }
307 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
308 LOG(INFO) << "Update succeeded -- reboot needed.";
309 exit(0);
310 }
311 return TRUE;
312}
313
314// This is similar to watching for updates but rather than registering
315// a signal watch, activelly poll the daemon just in case it stops
316// sending notifications.
317void CompleteUpdate() {
318 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
319 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
320 g_main_loop_run(loop);
321 g_main_loop_unref(loop);
322}
323
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700324} // namespace {}
325
326int main(int argc, char** argv) {
327 // Boilerplate init commands.
328 g_type_init();
Ben Chan46bf5c82013-06-24 11:17:41 -0700329 dbus_threads_init_default();
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700330 chromeos_update_engine::Subprocess::Init();
331 google::ParseCommandLineFlags(&argc, &argv, true);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700332
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700333 // Update the status if requested.
334 if (FLAGS_reset_status) {
335 LOG(INFO) << "Setting Update Engine status to idle ...";
336 if (!ResetStatus()) {
337 LOG(ERROR) << "ResetStatus failed.";
338 return 1;
339 }
340
Gilad Arnold50c60632013-01-25 10:27:19 -0800341 LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n"
342 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} "
343 "| sed 's/^[^0-9]*//')-1)) $D;)";
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700344 return 0;
345 }
346
347
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700348 if (FLAGS_status) {
349 LOG(INFO) << "Querying Update Engine status...";
Darin Petkov58529db2010-08-13 09:19:47 -0700350 if (!GetStatus(NULL)) {
351 LOG(FATAL) << "GetStatus failed.";
352 return 1;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700353 }
354 return 0;
355 }
Darin Petkov58529db2010-08-13 09:19:47 -0700356
Alex Deymof4867c42013-06-28 14:41:39 -0700357 // Changes the current update over cellular network setting.
358 if (!FLAGS_update_over_cellular.empty()) {
359 gboolean allowed = FLAGS_update_over_cellular == "yes";
360 if (!allowed && FLAGS_update_over_cellular != "no") {
361 LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular
362 << "\". Please specify \"yes\" or \"no\".";
363 } else {
364 SetUpdateOverCellularPermission(allowed);
365 }
366 }
367
368 // Show the current update over cellular network setting.
369 if (FLAGS_show_update_over_cellular) {
370 bool allowed = GetUpdateOverCellularPermission();
371 LOG(INFO) << "Current update over cellular network setting: "
372 << (allowed ? "ENABLED" : "DISABLED");
373 }
374
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700375 // First, update the target channel if requested.
376 if (!FLAGS_channel.empty())
377 SetTargetChannel(FLAGS_channel);
Darin Petkov8daa3242010-10-25 13:28:47 -0700378
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700379 // Show the current and target channels if requested.
380 if (FLAGS_show_channel) {
381 string current_channel = GetChannel(true);
382 LOG(INFO) << "Current Channel: " << current_channel;
383
384 string target_channel = GetChannel(false);
385 if (!target_channel.empty())
386 LOG(INFO) << "Target Channel (pending update): " << target_channel;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900387 }
388
Chris Sosad317e402013-06-12 13:47:09 -0700389 bool do_update_request = FLAGS_check_for_update | FLAGS_update |
390 !FLAGS_app_version.empty() | !FLAGS_omaha_url.empty();
391
392 if (!FLAGS_powerwash && !FLAGS_rollback) {
393 LOG(FATAL) << "Skipping powerwash only works with rollback";
394 return 1;
395 }
396
397 if (do_update_request && FLAGS_rollback) {
398 LOG(FATAL) << "Update should not be requested with rollback!";
399 return 1;
400 }
401
402 if(FLAGS_rollback) {
403 LOG(INFO) << "Requesting rollback.";
404 CHECK(Rollback(FLAGS_powerwash)) << "Request for rollback failed.";
405 return 0;
406 }
407
Darin Petkov58529db2010-08-13 09:19:47 -0700408 // Initiate an update check, if necessary.
Chris Sosad317e402013-06-12 13:47:09 -0700409 if (do_update_request) {
Darin Petkov296889c2010-07-23 16:20:54 -0700410 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Darin Petkov58529db2010-08-13 09:19:47 -0700411 string app_version = FLAGS_app_version;
412 if (FLAGS_update && app_version.empty()) {
413 app_version = "ForcedUpdate";
414 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700415 }
Darin Petkov58529db2010-08-13 09:19:47 -0700416 LOG(INFO) << "Initiating update check and install.";
417 CHECK(CheckForUpdates(app_version, FLAGS_omaha_url))
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700418 << "Update check/initiate update failed.";
Darin Petkov58529db2010-08-13 09:19:47 -0700419
420 // Wait for an update to complete.
421 if (FLAGS_update) {
Darin Petkov9d911fa2010-08-19 09:36:08 -0700422 LOG(INFO) << "Waiting for update to complete.";
Darin Petkov58529db2010-08-13 09:19:47 -0700423 CompleteUpdate(); // Should never return.
424 return 1;
425 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700426 return 0;
427 }
Darin Petkov58529db2010-08-13 09:19:47 -0700428
429 // Start watching for updates.
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700430 if (FLAGS_watch_for_updates) {
Darin Petkov296889c2010-07-23 16:20:54 -0700431 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700432 LOG(INFO) << "Watching for status updates.";
433 WatchForUpdates(); // Should never return.
434 return 1;
435 }
Darin Petkov58529db2010-08-13 09:19:47 -0700436
Darin Petkov296889c2010-07-23 16:20:54 -0700437 if (FLAGS_reboot) {
438 LOG(INFO) << "Requesting a reboot...";
439 CHECK(RebootIfNeeded());
440 return 0;
441 }
Andrew de los Reyesada42202010-07-15 22:23:20 -0700442
Darin Petkov8daa3242010-10-25 13:28:47 -0700443 LOG(INFO) << "Done.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700444 return 0;
445}