blob: 87b16158cd779889065053b881810861ad1210a2 [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.");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070042
43namespace {
44
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070045bool GetProxy(DBusGProxy** out_proxy) {
46 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070047 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070048 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070049 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070050 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070051
52 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Richard Barnetted7936062013-01-18 13:38:51 -080053 if (bus == NULL) {
54 LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error);
55 exit(1);
56 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070057 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070058 if (i > 0) {
59 LOG(INFO) << "Retrying to get dbus proxy. Try "
60 << (i + 1) << "/" << kTries;
Gilad Arnold8e3f1262013-01-08 14:59:54 -080061 g_usleep(kRetrySeconds * G_USEC_PER_SEC);
Darin Petkova0b9e772011-10-06 05:05:56 -070062 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070063 proxy = dbus_g_proxy_new_for_name_owner(bus,
64 kUpdateEngineServiceName,
65 kUpdateEngineServicePath,
66 kUpdateEngineServiceInterface,
67 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070068 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
69 << kUpdateEngineServiceName << ": "
70 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070071 }
Richard Barnetted7936062013-01-18 13:38:51 -080072 if (proxy == NULL) {
73 LOG(ERROR) << "Giving up -- unable to get dbus proxy for "
74 << kUpdateEngineServiceName;
75 exit(1);
76 }
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070077 *out_proxy = proxy;
78 return true;
79}
80
81static void StatusUpdateSignalHandler(DBusGProxy* proxy,
82 int64_t last_checked_time,
83 double progress,
84 gchar* current_operation,
85 gchar* new_version,
86 int64_t new_size,
87 void* user_data) {
88 LOG(INFO) << "Got status update:";
89 LOG(INFO) << " last_checked_time: " << last_checked_time;
90 LOG(INFO) << " progress: " << progress;
91 LOG(INFO) << " current_operation: " << current_operation;
92 LOG(INFO) << " new_version: " << new_version;
93 LOG(INFO) << " new_size: " << new_size;
94}
95
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -070096bool ResetStatus() {
97 DBusGProxy* proxy;
98 GError* error = NULL;
99
100 CHECK(GetProxy(&proxy));
101
102 gboolean rc =
103 org_chromium_UpdateEngineInterface_reset_status(proxy, &error);
104 return rc;
105}
106
107
Darin Petkov58529db2010-08-13 09:19:47 -0700108// If |op| is non-NULL, sets it to the current operation string or an
109// empty string if unable to obtain the current status.
110bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700111 DBusGProxy* proxy;
112 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700113
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700114 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700115
116 gint64 last_checked_time = 0;
117 gdouble progress = 0.0;
118 char* current_op = NULL;
119 char* new_version = NULL;
120 gint64 new_size = 0;
121
122 gboolean rc = org_chromium_UpdateEngineInterface_get_status(
123 proxy,
124 &last_checked_time,
125 &progress,
126 &current_op,
127 &new_version,
128 &new_size,
129 &error);
130 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700131 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700132 }
133 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
134 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
135 last_checked_time,
136 progress,
137 current_op,
138 new_version,
139 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700140 if (op) {
141 *op = current_op ? current_op : "";
142 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700143 return true;
144}
145
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700146// Should never return.
147void WatchForUpdates() {
148 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700149
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700150 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700151
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700152 // Register marshaller
153 dbus_g_object_register_marshaller(
154 update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
155 G_TYPE_NONE,
156 G_TYPE_INT64,
157 G_TYPE_DOUBLE,
158 G_TYPE_STRING,
159 G_TYPE_STRING,
160 G_TYPE_INT64,
161 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700162
163 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700164 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700165 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700166 G_TYPE_INT64,
167 G_TYPE_DOUBLE,
168 G_TYPE_STRING,
169 G_TYPE_STRING,
170 G_TYPE_INT64,
171 G_TYPE_INVALID);
172 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
173 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700174 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700175 G_CALLBACK(StatusUpdateSignalHandler),
176 NULL,
177 NULL);
178 g_main_loop_run(loop);
179 g_main_loop_unref(loop);
180}
181
Chris Sosad317e402013-06-12 13:47:09 -0700182bool Rollback(bool rollback) {
183 DBusGProxy* proxy;
184 GError* error = NULL;
185
186 CHECK(GetProxy(&proxy));
187
188 gboolean rc =
189 org_chromium_UpdateEngineInterface_attempt_rollback(proxy,
190 rollback,
191 &error);
192 CHECK_EQ(rc, TRUE) << "Error with rollback request: "
193 << GetAndFreeGError(&error);
194 return true;
195}
196
Darin Petkov58529db2010-08-13 09:19:47 -0700197bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700198 DBusGProxy* proxy;
199 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700200
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700201 CHECK(GetProxy(&proxy));
202
203 gboolean rc =
Darin Petkov5a7f5652010-07-22 21:40:09 -0700204 org_chromium_UpdateEngineInterface_attempt_update(proxy,
205 app_version.c_str(),
206 omaha_url.c_str(),
207 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700208 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700209 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700210 return true;
211}
212
Darin Petkov296889c2010-07-23 16:20:54 -0700213bool RebootIfNeeded() {
214 DBusGProxy* proxy;
215 GError* error = NULL;
216
217 CHECK(GetProxy(&proxy));
218
219 gboolean rc =
220 org_chromium_UpdateEngineInterface_reboot_if_needed(proxy, &error);
221 // Reboot error code doesn't necessarily mean that a reboot
222 // failed. For example, D-Bus may be shutdown before we receive the
223 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700224 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700225 return true;
226}
227
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700228void SetTargetChannel(const string& target_channel) {
Darin Petkov8daa3242010-10-25 13:28:47 -0700229 DBusGProxy* proxy;
230 GError* error = NULL;
231
232 CHECK(GetProxy(&proxy));
233
234 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700235 org_chromium_UpdateEngineInterface_set_channel(proxy,
236 target_channel.c_str(),
237 true, // OK to Powerwash
238 &error);
239 CHECK_EQ(rc, true) << "Error setting the channel: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700240 << GetAndFreeGError(&error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700241 LOG(INFO) << "Channel permanently set to: " << target_channel;
Darin Petkov8daa3242010-10-25 13:28:47 -0700242}
243
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700244string GetChannel(bool get_current_channel) {
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900245 DBusGProxy* proxy;
246 GError* error = NULL;
247
248 CHECK(GetProxy(&proxy));
249
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700250 char* channel = NULL;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900251 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700252 org_chromium_UpdateEngineInterface_get_channel(proxy,
253 get_current_channel,
254 &channel,
255 &error);
256 CHECK_EQ(rc, true) << "Error getting the channel: "
257 << GetAndFreeGError(&error);
258 string output = channel;
259 g_free(channel);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900260 return output;
261}
262
Darin Petkov58529db2010-08-13 09:19:47 -0700263static gboolean CompleteUpdateSource(gpointer data) {
264 string current_op;
265 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
266 LOG(ERROR) << "Update failed.";
267 exit(1);
268 }
269 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
270 LOG(INFO) << "Update succeeded -- reboot needed.";
271 exit(0);
272 }
273 return TRUE;
274}
275
276// This is similar to watching for updates but rather than registering
277// a signal watch, activelly poll the daemon just in case it stops
278// sending notifications.
279void CompleteUpdate() {
280 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
281 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
282 g_main_loop_run(loop);
283 g_main_loop_unref(loop);
284}
285
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700286} // namespace {}
287
288int main(int argc, char** argv) {
289 // Boilerplate init commands.
290 g_type_init();
Ben Chan46bf5c82013-06-24 11:17:41 -0700291 dbus_threads_init_default();
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700292 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}