blob: b4a4bda5d60eb43dbfc27e092583f2438c914f95 [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
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -07007#include <gflags/gflags.h>
8#include <glib.h>
9
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070010#include "update_engine/marshal.glibmarshal.h"
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070011#include "update_engine/dbus_constants.h"
12#include "update_engine/subprocess.h"
13#include "update_engine/utils.h"
14
15extern "C" {
16#include "update_engine/update_engine.dbusclient.h"
17}
18
19using chromeos_update_engine::kUpdateEngineServiceName;
20using chromeos_update_engine::kUpdateEngineServicePath;
21using chromeos_update_engine::kUpdateEngineServiceInterface;
Darin Petkova0b9e772011-10-06 05:05:56 -070022using chromeos_update_engine::utils::GetAndFreeGError;
Darin Petkov5a7f5652010-07-22 21:40:09 -070023using std::string;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070024
Darin Petkov296889c2010-07-23 16:20:54 -070025DEFINE_string(app_version, "", "Force the current app version.");
Jay Srinivasanae4697c2013-03-18 17:08:08 -070026DEFINE_string(channel, "",
27 "Set the target channel. The device will be powerwashed if the target "
28 "channel is more stable than the current channel.");
Chris Sosad317e402013-06-12 13:47:09 -070029DEFINE_bool(check_for_update, false, "Initiate check for updates.");
30DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
31DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
32DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
33DEFINE_bool(rollback, false, "Perform a rollback to the previous partition.");
34DEFINE_bool(show_channel, false, "Show the current and target channels.");
35DEFINE_bool(powerwash, true, "When performing a rollback, do a powerwash.");
36DEFINE_bool(status, false, "Print the status to stdout.");
Darin Petkov58529db2010-08-13 09:19:47 -070037DEFINE_bool(update, false, "Forces an update and waits for its completion. "
38 "Exit status is 0 if the update succeeded, and 1 otherwise.");
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070039DEFINE_bool(watch_for_updates, false,
40 "Listen for status updates and print them to the screen.");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070041
42namespace {
43
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070044bool GetProxy(DBusGProxy** out_proxy) {
45 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070046 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070047 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070048 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070049 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070050
51 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Richard Barnetted7936062013-01-18 13:38:51 -080052 if (bus == NULL) {
53 LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error);
54 exit(1);
55 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070056 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070057 if (i > 0) {
58 LOG(INFO) << "Retrying to get dbus proxy. Try "
59 << (i + 1) << "/" << kTries;
Gilad Arnold8e3f1262013-01-08 14:59:54 -080060 g_usleep(kRetrySeconds * G_USEC_PER_SEC);
Darin Petkova0b9e772011-10-06 05:05:56 -070061 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070062 proxy = dbus_g_proxy_new_for_name_owner(bus,
63 kUpdateEngineServiceName,
64 kUpdateEngineServicePath,
65 kUpdateEngineServiceInterface,
66 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070067 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
68 << kUpdateEngineServiceName << ": "
69 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070070 }
Richard Barnetted7936062013-01-18 13:38:51 -080071 if (proxy == NULL) {
72 LOG(ERROR) << "Giving up -- unable to get dbus proxy for "
73 << kUpdateEngineServiceName;
74 exit(1);
75 }
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070076 *out_proxy = proxy;
77 return true;
78}
79
80static void StatusUpdateSignalHandler(DBusGProxy* proxy,
81 int64_t last_checked_time,
82 double progress,
83 gchar* current_operation,
84 gchar* new_version,
85 int64_t new_size,
86 void* user_data) {
87 LOG(INFO) << "Got status update:";
88 LOG(INFO) << " last_checked_time: " << last_checked_time;
89 LOG(INFO) << " progress: " << progress;
90 LOG(INFO) << " current_operation: " << current_operation;
91 LOG(INFO) << " new_version: " << new_version;
92 LOG(INFO) << " new_size: " << new_size;
93}
94
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -070095bool ResetStatus() {
96 DBusGProxy* proxy;
97 GError* error = NULL;
98
99 CHECK(GetProxy(&proxy));
100
101 gboolean rc =
102 org_chromium_UpdateEngineInterface_reset_status(proxy, &error);
103 return rc;
104}
105
106
Darin Petkov58529db2010-08-13 09:19:47 -0700107// If |op| is non-NULL, sets it to the current operation string or an
108// empty string if unable to obtain the current status.
109bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700110 DBusGProxy* proxy;
111 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700112
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700113 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700114
115 gint64 last_checked_time = 0;
116 gdouble progress = 0.0;
117 char* current_op = NULL;
118 char* new_version = NULL;
119 gint64 new_size = 0;
120
121 gboolean rc = org_chromium_UpdateEngineInterface_get_status(
122 proxy,
123 &last_checked_time,
124 &progress,
125 &current_op,
126 &new_version,
127 &new_size,
128 &error);
129 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700130 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700131 }
132 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
133 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
134 last_checked_time,
135 progress,
136 current_op,
137 new_version,
138 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700139 if (op) {
140 *op = current_op ? current_op : "";
141 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700142 return true;
143}
144
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700145// Should never return.
146void WatchForUpdates() {
147 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700148
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700149 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700150
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700151 // Register marshaller
152 dbus_g_object_register_marshaller(
153 update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
154 G_TYPE_NONE,
155 G_TYPE_INT64,
156 G_TYPE_DOUBLE,
157 G_TYPE_STRING,
158 G_TYPE_STRING,
159 G_TYPE_INT64,
160 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700161
162 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700163 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700164 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700165 G_TYPE_INT64,
166 G_TYPE_DOUBLE,
167 G_TYPE_STRING,
168 G_TYPE_STRING,
169 G_TYPE_INT64,
170 G_TYPE_INVALID);
171 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
172 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700173 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700174 G_CALLBACK(StatusUpdateSignalHandler),
175 NULL,
176 NULL);
177 g_main_loop_run(loop);
178 g_main_loop_unref(loop);
179}
180
Chris Sosad317e402013-06-12 13:47:09 -0700181bool Rollback(bool rollback) {
182 DBusGProxy* proxy;
183 GError* error = NULL;
184
185 CHECK(GetProxy(&proxy));
186
187 gboolean rc =
188 org_chromium_UpdateEngineInterface_attempt_rollback(proxy,
189 rollback,
190 &error);
191 CHECK_EQ(rc, TRUE) << "Error with rollback request: "
192 << GetAndFreeGError(&error);
193 return true;
194}
195
Darin Petkov58529db2010-08-13 09:19:47 -0700196bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700197 DBusGProxy* proxy;
198 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700199
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700200 CHECK(GetProxy(&proxy));
201
202 gboolean rc =
Darin Petkov5a7f5652010-07-22 21:40:09 -0700203 org_chromium_UpdateEngineInterface_attempt_update(proxy,
204 app_version.c_str(),
205 omaha_url.c_str(),
206 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700207 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700208 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700209 return true;
210}
211
Darin Petkov296889c2010-07-23 16:20:54 -0700212bool RebootIfNeeded() {
213 DBusGProxy* proxy;
214 GError* error = NULL;
215
216 CHECK(GetProxy(&proxy));
217
218 gboolean rc =
219 org_chromium_UpdateEngineInterface_reboot_if_needed(proxy, &error);
220 // Reboot error code doesn't necessarily mean that a reboot
221 // failed. For example, D-Bus may be shutdown before we receive the
222 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700223 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700224 return true;
225}
226
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700227void SetTargetChannel(const string& target_channel) {
Darin Petkov8daa3242010-10-25 13:28:47 -0700228 DBusGProxy* proxy;
229 GError* error = NULL;
230
231 CHECK(GetProxy(&proxy));
232
233 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700234 org_chromium_UpdateEngineInterface_set_channel(proxy,
235 target_channel.c_str(),
236 true, // OK to Powerwash
237 &error);
238 CHECK_EQ(rc, true) << "Error setting the channel: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700239 << GetAndFreeGError(&error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700240 LOG(INFO) << "Channel permanently set to: " << target_channel;
Darin Petkov8daa3242010-10-25 13:28:47 -0700241}
242
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700243string GetChannel(bool get_current_channel) {
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900244 DBusGProxy* proxy;
245 GError* error = NULL;
246
247 CHECK(GetProxy(&proxy));
248
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700249 char* channel = NULL;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900250 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700251 org_chromium_UpdateEngineInterface_get_channel(proxy,
252 get_current_channel,
253 &channel,
254 &error);
255 CHECK_EQ(rc, true) << "Error getting the channel: "
256 << GetAndFreeGError(&error);
257 string output = channel;
258 g_free(channel);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900259 return output;
260}
261
Darin Petkov58529db2010-08-13 09:19:47 -0700262static gboolean CompleteUpdateSource(gpointer data) {
263 string current_op;
264 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
265 LOG(ERROR) << "Update failed.";
266 exit(1);
267 }
268 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
269 LOG(INFO) << "Update succeeded -- reboot needed.";
270 exit(0);
271 }
272 return TRUE;
273}
274
275// This is similar to watching for updates but rather than registering
276// a signal watch, activelly poll the daemon just in case it stops
277// sending notifications.
278void CompleteUpdate() {
279 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
280 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
281 g_main_loop_run(loop);
282 g_main_loop_unref(loop);
283}
284
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700285} // namespace {}
286
287int main(int argc, char** argv) {
288 // Boilerplate init commands.
289 g_type_init();
290 g_thread_init(NULL);
291 dbus_g_thread_init();
292 chromeos_update_engine::Subprocess::Init();
293 google::ParseCommandLineFlags(&argc, &argv, true);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700294
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700295 // Update the status if requested.
296 if (FLAGS_reset_status) {
297 LOG(INFO) << "Setting Update Engine status to idle ...";
298 if (!ResetStatus()) {
299 LOG(ERROR) << "ResetStatus failed.";
300 return 1;
301 }
302
Gilad Arnold50c60632013-01-25 10:27:19 -0800303 LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n"
304 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} "
305 "| sed 's/^[^0-9]*//')-1)) $D;)";
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700306 return 0;
307 }
308
309
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700310 if (FLAGS_status) {
311 LOG(INFO) << "Querying Update Engine status...";
Darin Petkov58529db2010-08-13 09:19:47 -0700312 if (!GetStatus(NULL)) {
313 LOG(FATAL) << "GetStatus failed.";
314 return 1;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700315 }
316 return 0;
317 }
Darin Petkov58529db2010-08-13 09:19:47 -0700318
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700319 // First, update the target channel if requested.
320 if (!FLAGS_channel.empty())
321 SetTargetChannel(FLAGS_channel);
Darin Petkov8daa3242010-10-25 13:28:47 -0700322
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700323 // Show the current and target channels if requested.
324 if (FLAGS_show_channel) {
325 string current_channel = GetChannel(true);
326 LOG(INFO) << "Current Channel: " << current_channel;
327
328 string target_channel = GetChannel(false);
329 if (!target_channel.empty())
330 LOG(INFO) << "Target Channel (pending update): " << target_channel;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900331 }
332
Chris Sosad317e402013-06-12 13:47:09 -0700333 bool do_update_request = FLAGS_check_for_update | FLAGS_update |
334 !FLAGS_app_version.empty() | !FLAGS_omaha_url.empty();
335
336 if (!FLAGS_powerwash && !FLAGS_rollback) {
337 LOG(FATAL) << "Skipping powerwash only works with rollback";
338 return 1;
339 }
340
341 if (do_update_request && FLAGS_rollback) {
342 LOG(FATAL) << "Update should not be requested with rollback!";
343 return 1;
344 }
345
346 if(FLAGS_rollback) {
347 LOG(INFO) << "Requesting rollback.";
348 CHECK(Rollback(FLAGS_powerwash)) << "Request for rollback failed.";
349 return 0;
350 }
351
Darin Petkov58529db2010-08-13 09:19:47 -0700352 // Initiate an update check, if necessary.
Chris Sosad317e402013-06-12 13:47:09 -0700353 if (do_update_request) {
Darin Petkov296889c2010-07-23 16:20:54 -0700354 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Darin Petkov58529db2010-08-13 09:19:47 -0700355 string app_version = FLAGS_app_version;
356 if (FLAGS_update && app_version.empty()) {
357 app_version = "ForcedUpdate";
358 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700359 }
Darin Petkov58529db2010-08-13 09:19:47 -0700360 LOG(INFO) << "Initiating update check and install.";
361 CHECK(CheckForUpdates(app_version, FLAGS_omaha_url))
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700362 << "Update check/initiate update failed.";
Darin Petkov58529db2010-08-13 09:19:47 -0700363
364 // Wait for an update to complete.
365 if (FLAGS_update) {
Darin Petkov9d911fa2010-08-19 09:36:08 -0700366 LOG(INFO) << "Waiting for update to complete.";
Darin Petkov58529db2010-08-13 09:19:47 -0700367 CompleteUpdate(); // Should never return.
368 return 1;
369 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700370 return 0;
371 }
Darin Petkov58529db2010-08-13 09:19:47 -0700372
373 // Start watching for updates.
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700374 if (FLAGS_watch_for_updates) {
Darin Petkov296889c2010-07-23 16:20:54 -0700375 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700376 LOG(INFO) << "Watching for status updates.";
377 WatchForUpdates(); // Should never return.
378 return 1;
379 }
Darin Petkov58529db2010-08-13 09:19:47 -0700380
Darin Petkov296889c2010-07-23 16:20:54 -0700381 if (FLAGS_reboot) {
382 LOG(INFO) << "Requesting a reboot...";
383 CHECK(RebootIfNeeded());
384 return 0;
385 }
Andrew de los Reyesada42202010-07-15 22:23:20 -0700386
Darin Petkov8daa3242010-10-25 13:28:47 -0700387 LOG(INFO) << "Done.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700388 return 0;
389}