blob: e959534c128c7e1824cc4b77b0e044a7e586c2d4 [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.");
Chris Sosacb7fa882013-07-25 17:02:59 -070036DEFINE_bool(powerwash, true, "When performing rollback or channel change, "
37 "do a powerwash or allow it respectively.");
Chris Sosad317e402013-06-12 13:47:09 -070038DEFINE_bool(status, false, "Print the status to stdout.");
Darin Petkov58529db2010-08-13 09:19:47 -070039DEFINE_bool(update, false, "Forces an update and waits for its completion. "
40 "Exit status is 0 if the update succeeded, and 1 otherwise.");
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070041DEFINE_bool(watch_for_updates, false,
42 "Listen for status updates and print them to the screen.");
Alex Deymof4867c42013-06-28 14:41:39 -070043DEFINE_bool(show_update_over_cellular, false,
44 "Show the current setting for updates over cellular networks.");
45DEFINE_string(update_over_cellular, "",
46 "Enables (\"yes\") or disables (\"no\") the updates over "
47 "cellular networks.");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070048
49namespace {
50
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070051bool GetProxy(DBusGProxy** out_proxy) {
52 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070053 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070054 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070055 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070056 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070057
58 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Richard Barnetted7936062013-01-18 13:38:51 -080059 if (bus == NULL) {
60 LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error);
61 exit(1);
62 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070063 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070064 if (i > 0) {
65 LOG(INFO) << "Retrying to get dbus proxy. Try "
66 << (i + 1) << "/" << kTries;
Gilad Arnold8e3f1262013-01-08 14:59:54 -080067 g_usleep(kRetrySeconds * G_USEC_PER_SEC);
Darin Petkova0b9e772011-10-06 05:05:56 -070068 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070069 proxy = dbus_g_proxy_new_for_name_owner(bus,
70 kUpdateEngineServiceName,
71 kUpdateEngineServicePath,
72 kUpdateEngineServiceInterface,
73 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070074 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
75 << kUpdateEngineServiceName << ": "
76 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070077 }
Richard Barnetted7936062013-01-18 13:38:51 -080078 if (proxy == NULL) {
79 LOG(ERROR) << "Giving up -- unable to get dbus proxy for "
80 << kUpdateEngineServiceName;
81 exit(1);
82 }
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070083 *out_proxy = proxy;
84 return true;
85}
86
87static void StatusUpdateSignalHandler(DBusGProxy* proxy,
88 int64_t last_checked_time,
89 double progress,
90 gchar* current_operation,
91 gchar* new_version,
92 int64_t new_size,
93 void* user_data) {
94 LOG(INFO) << "Got status update:";
95 LOG(INFO) << " last_checked_time: " << last_checked_time;
96 LOG(INFO) << " progress: " << progress;
97 LOG(INFO) << " current_operation: " << current_operation;
98 LOG(INFO) << " new_version: " << new_version;
99 LOG(INFO) << " new_size: " << new_size;
100}
101
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700102bool ResetStatus() {
103 DBusGProxy* proxy;
104 GError* error = NULL;
105
106 CHECK(GetProxy(&proxy));
107
108 gboolean rc =
109 org_chromium_UpdateEngineInterface_reset_status(proxy, &error);
110 return rc;
111}
112
113
Darin Petkov58529db2010-08-13 09:19:47 -0700114// If |op| is non-NULL, sets it to the current operation string or an
115// empty string if unable to obtain the current status.
116bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700117 DBusGProxy* proxy;
118 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700119
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700120 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700121
122 gint64 last_checked_time = 0;
123 gdouble progress = 0.0;
124 char* current_op = NULL;
125 char* new_version = NULL;
126 gint64 new_size = 0;
127
128 gboolean rc = org_chromium_UpdateEngineInterface_get_status(
129 proxy,
130 &last_checked_time,
131 &progress,
132 &current_op,
133 &new_version,
134 &new_size,
135 &error);
136 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700137 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700138 }
139 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
140 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
141 last_checked_time,
142 progress,
143 current_op,
144 new_version,
145 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700146 if (op) {
147 *op = current_op ? current_op : "";
148 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700149 return true;
150}
151
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700152// Should never return.
153void WatchForUpdates() {
154 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700155
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700156 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700157
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700158 // Register marshaller
159 dbus_g_object_register_marshaller(
160 update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
161 G_TYPE_NONE,
162 G_TYPE_INT64,
163 G_TYPE_DOUBLE,
164 G_TYPE_STRING,
165 G_TYPE_STRING,
166 G_TYPE_INT64,
167 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700168
169 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700170 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700171 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700172 G_TYPE_INT64,
173 G_TYPE_DOUBLE,
174 G_TYPE_STRING,
175 G_TYPE_STRING,
176 G_TYPE_INT64,
177 G_TYPE_INVALID);
178 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
179 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700180 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700181 G_CALLBACK(StatusUpdateSignalHandler),
182 NULL,
183 NULL);
184 g_main_loop_run(loop);
185 g_main_loop_unref(loop);
186}
187
Chris Sosad317e402013-06-12 13:47:09 -0700188bool Rollback(bool rollback) {
189 DBusGProxy* proxy;
190 GError* error = NULL;
191
192 CHECK(GetProxy(&proxy));
193
194 gboolean rc =
195 org_chromium_UpdateEngineInterface_attempt_rollback(proxy,
196 rollback,
197 &error);
198 CHECK_EQ(rc, TRUE) << "Error with rollback request: "
199 << GetAndFreeGError(&error);
200 return true;
201}
202
Darin Petkov58529db2010-08-13 09:19:47 -0700203bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700204 DBusGProxy* proxy;
205 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700206
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700207 CHECK(GetProxy(&proxy));
208
209 gboolean rc =
Darin Petkov5a7f5652010-07-22 21:40:09 -0700210 org_chromium_UpdateEngineInterface_attempt_update(proxy,
211 app_version.c_str(),
212 omaha_url.c_str(),
213 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700214 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700215 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700216 return true;
217}
218
Darin Petkov296889c2010-07-23 16:20:54 -0700219bool RebootIfNeeded() {
220 DBusGProxy* proxy;
221 GError* error = NULL;
222
223 CHECK(GetProxy(&proxy));
224
225 gboolean rc =
226 org_chromium_UpdateEngineInterface_reboot_if_needed(proxy, &error);
227 // Reboot error code doesn't necessarily mean that a reboot
228 // failed. For example, D-Bus may be shutdown before we receive the
229 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700230 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700231 return true;
232}
233
Chris Sosacb7fa882013-07-25 17:02:59 -0700234void SetTargetChannel(const string& target_channel, bool allow_powerwash) {
Darin Petkov8daa3242010-10-25 13:28:47 -0700235 DBusGProxy* proxy;
236 GError* error = NULL;
237
238 CHECK(GetProxy(&proxy));
239
240 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700241 org_chromium_UpdateEngineInterface_set_channel(proxy,
242 target_channel.c_str(),
Chris Sosacb7fa882013-07-25 17:02:59 -0700243 allow_powerwash,
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700244 &error);
245 CHECK_EQ(rc, true) << "Error setting the channel: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700246 << GetAndFreeGError(&error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700247 LOG(INFO) << "Channel permanently set to: " << target_channel;
Darin Petkov8daa3242010-10-25 13:28:47 -0700248}
249
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700250string GetChannel(bool get_current_channel) {
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900251 DBusGProxy* proxy;
252 GError* error = NULL;
253
254 CHECK(GetProxy(&proxy));
255
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700256 char* channel = NULL;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900257 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700258 org_chromium_UpdateEngineInterface_get_channel(proxy,
259 get_current_channel,
260 &channel,
261 &error);
262 CHECK_EQ(rc, true) << "Error getting the channel: "
263 << GetAndFreeGError(&error);
264 string output = channel;
265 g_free(channel);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900266 return output;
267}
268
Alex Deymof4867c42013-06-28 14:41:39 -0700269bool SetUpdateOverCellularPermission(gboolean allowed) {
270 DBusGProxy* proxy;
271 GError* error = NULL;
272
273 CHECK(GetProxy(&proxy));
274
275 gboolean rc =
276 org_chromium_UpdateEngineInterface_set_update_over_cellular_permission(
277 proxy,
278 allowed,
279 &error);
280 CHECK_EQ(rc, true) << "Error setting the update over cellular setting: "
281 << GetAndFreeGError(&error);
282 return true;
283}
284
285bool GetUpdateOverCellularPermission() {
286 DBusGProxy* proxy;
287 GError* error = NULL;
288
289 CHECK(GetProxy(&proxy));
290
291 gboolean allowed;
292 gboolean rc =
293 org_chromium_UpdateEngineInterface_get_update_over_cellular_permission(
294 proxy,
295 &allowed,
296 &error);
297 CHECK_EQ(rc, true) << "Error getting the update over cellular setting: "
298 << GetAndFreeGError(&error);
299 return allowed;
300}
301
Darin Petkov58529db2010-08-13 09:19:47 -0700302static gboolean CompleteUpdateSource(gpointer data) {
303 string current_op;
304 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
305 LOG(ERROR) << "Update failed.";
306 exit(1);
307 }
308 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
309 LOG(INFO) << "Update succeeded -- reboot needed.";
310 exit(0);
311 }
312 return TRUE;
313}
314
315// This is similar to watching for updates but rather than registering
316// a signal watch, activelly poll the daemon just in case it stops
317// sending notifications.
318void CompleteUpdate() {
319 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
320 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
321 g_main_loop_run(loop);
322 g_main_loop_unref(loop);
323}
324
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700325} // namespace {}
326
327int main(int argc, char** argv) {
328 // Boilerplate init commands.
329 g_type_init();
Ben Chan46bf5c82013-06-24 11:17:41 -0700330 dbus_threads_init_default();
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700331 chromeos_update_engine::Subprocess::Init();
332 google::ParseCommandLineFlags(&argc, &argv, true);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700333
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700334 // Update the status if requested.
335 if (FLAGS_reset_status) {
336 LOG(INFO) << "Setting Update Engine status to idle ...";
337 if (!ResetStatus()) {
338 LOG(ERROR) << "ResetStatus failed.";
339 return 1;
340 }
341
Gilad Arnold50c60632013-01-25 10:27:19 -0800342 LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n"
343 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} "
344 "| sed 's/^[^0-9]*//')-1)) $D;)";
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700345 return 0;
346 }
347
348
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700349 if (FLAGS_status) {
350 LOG(INFO) << "Querying Update Engine status...";
Darin Petkov58529db2010-08-13 09:19:47 -0700351 if (!GetStatus(NULL)) {
352 LOG(FATAL) << "GetStatus failed.";
353 return 1;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700354 }
355 return 0;
356 }
Darin Petkov58529db2010-08-13 09:19:47 -0700357
Alex Deymof4867c42013-06-28 14:41:39 -0700358 // Changes the current update over cellular network setting.
359 if (!FLAGS_update_over_cellular.empty()) {
360 gboolean allowed = FLAGS_update_over_cellular == "yes";
361 if (!allowed && FLAGS_update_over_cellular != "no") {
362 LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular
363 << "\". Please specify \"yes\" or \"no\".";
364 } else {
365 SetUpdateOverCellularPermission(allowed);
366 }
367 }
368
369 // Show the current update over cellular network setting.
370 if (FLAGS_show_update_over_cellular) {
371 bool allowed = GetUpdateOverCellularPermission();
372 LOG(INFO) << "Current update over cellular network setting: "
373 << (allowed ? "ENABLED" : "DISABLED");
374 }
375
Chris Sosacb7fa882013-07-25 17:02:59 -0700376 if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) {
377 LOG(FATAL) << "powerwash flag only makes sense rollback or channel change";
378 return 1;
379 }
380
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700381 // First, update the target channel if requested.
382 if (!FLAGS_channel.empty())
Chris Sosacb7fa882013-07-25 17:02:59 -0700383 SetTargetChannel(FLAGS_channel, FLAGS_powerwash);
Darin Petkov8daa3242010-10-25 13:28:47 -0700384
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700385 // Show the current and target channels if requested.
386 if (FLAGS_show_channel) {
387 string current_channel = GetChannel(true);
388 LOG(INFO) << "Current Channel: " << current_channel;
389
390 string target_channel = GetChannel(false);
391 if (!target_channel.empty())
392 LOG(INFO) << "Target Channel (pending update): " << target_channel;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900393 }
394
Chris Sosad317e402013-06-12 13:47:09 -0700395 bool do_update_request = FLAGS_check_for_update | FLAGS_update |
396 !FLAGS_app_version.empty() | !FLAGS_omaha_url.empty();
397
Chris Sosad317e402013-06-12 13:47:09 -0700398 if (do_update_request && FLAGS_rollback) {
399 LOG(FATAL) << "Update should not be requested with rollback!";
400 return 1;
401 }
402
403 if(FLAGS_rollback) {
404 LOG(INFO) << "Requesting rollback.";
405 CHECK(Rollback(FLAGS_powerwash)) << "Request for rollback failed.";
406 return 0;
407 }
408
Darin Petkov58529db2010-08-13 09:19:47 -0700409 // Initiate an update check, if necessary.
Chris Sosad317e402013-06-12 13:47:09 -0700410 if (do_update_request) {
Darin Petkov296889c2010-07-23 16:20:54 -0700411 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Darin Petkov58529db2010-08-13 09:19:47 -0700412 string app_version = FLAGS_app_version;
413 if (FLAGS_update && app_version.empty()) {
414 app_version = "ForcedUpdate";
415 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700416 }
Darin Petkov58529db2010-08-13 09:19:47 -0700417 LOG(INFO) << "Initiating update check and install.";
418 CHECK(CheckForUpdates(app_version, FLAGS_omaha_url))
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700419 << "Update check/initiate update failed.";
Darin Petkov58529db2010-08-13 09:19:47 -0700420
421 // Wait for an update to complete.
422 if (FLAGS_update) {
Darin Petkov9d911fa2010-08-19 09:36:08 -0700423 LOG(INFO) << "Waiting for update to complete.";
Darin Petkov58529db2010-08-13 09:19:47 -0700424 CompleteUpdate(); // Should never return.
425 return 1;
426 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700427 return 0;
428 }
Darin Petkov58529db2010-08-13 09:19:47 -0700429
430 // Start watching for updates.
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700431 if (FLAGS_watch_for_updates) {
Darin Petkov296889c2010-07-23 16:20:54 -0700432 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700433 LOG(INFO) << "Watching for status updates.";
434 WatchForUpdates(); // Should never return.
435 return 1;
436 }
Darin Petkov58529db2010-08-13 09:19:47 -0700437
Darin Petkov296889c2010-07-23 16:20:54 -0700438 if (FLAGS_reboot) {
439 LOG(INFO) << "Requesting a reboot...";
440 CHECK(RebootIfNeeded());
441 return 0;
442 }
Andrew de los Reyesada42202010-07-15 22:23:20 -0700443
Darin Petkov8daa3242010-10-25 13:28:47 -0700444 LOG(INFO) << "Done.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700445 return 0;
446}