blob: e28801ca9fc4100236ced07d6777e7aab12990b9 [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.");
26DEFINE_bool(check_for_update, false, "Initiate check for updates.");
Darin Petkov296889c2010-07-23 16:20:54 -070027DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
28DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
Jay Srinivasanae4697c2013-03-18 17:08:08 -070029DEFINE_bool(show_channel, false, "Show the current and target channels.");
Darin Petkov5a7f5652010-07-22 21:40:09 -070030DEFINE_bool(status, false, "Print the status to stdout.");
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -070031DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
Jay Srinivasanae4697c2013-03-18 17:08:08 -070032DEFINE_string(channel, "",
33 "Set the target channel. The device will be powerwashed if the target "
34 "channel is more stable than the current channel.");
Darin Petkov58529db2010-08-13 09:19:47 -070035DEFINE_bool(update, false, "Forces an update and waits for its completion. "
36 "Exit status is 0 if the update succeeded, and 1 otherwise.");
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070037DEFINE_bool(watch_for_updates, false,
38 "Listen for status updates and print them to the screen.");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070039
40namespace {
41
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070042bool GetProxy(DBusGProxy** out_proxy) {
43 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070044 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070045 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070046 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070047 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070048
49 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Richard Barnetted7936062013-01-18 13:38:51 -080050 if (bus == NULL) {
51 LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error);
52 exit(1);
53 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070054 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070055 if (i > 0) {
56 LOG(INFO) << "Retrying to get dbus proxy. Try "
57 << (i + 1) << "/" << kTries;
Gilad Arnold8e3f1262013-01-08 14:59:54 -080058 g_usleep(kRetrySeconds * G_USEC_PER_SEC);
Darin Petkova0b9e772011-10-06 05:05:56 -070059 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070060 proxy = dbus_g_proxy_new_for_name_owner(bus,
61 kUpdateEngineServiceName,
62 kUpdateEngineServicePath,
63 kUpdateEngineServiceInterface,
64 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070065 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
66 << kUpdateEngineServiceName << ": "
67 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070068 }
Richard Barnetted7936062013-01-18 13:38:51 -080069 if (proxy == NULL) {
70 LOG(ERROR) << "Giving up -- unable to get dbus proxy for "
71 << kUpdateEngineServiceName;
72 exit(1);
73 }
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070074 *out_proxy = proxy;
75 return true;
76}
77
78static void StatusUpdateSignalHandler(DBusGProxy* proxy,
79 int64_t last_checked_time,
80 double progress,
81 gchar* current_operation,
82 gchar* new_version,
83 int64_t new_size,
84 void* user_data) {
85 LOG(INFO) << "Got status update:";
86 LOG(INFO) << " last_checked_time: " << last_checked_time;
87 LOG(INFO) << " progress: " << progress;
88 LOG(INFO) << " current_operation: " << current_operation;
89 LOG(INFO) << " new_version: " << new_version;
90 LOG(INFO) << " new_size: " << new_size;
91}
92
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -070093bool ResetStatus() {
94 DBusGProxy* proxy;
95 GError* error = NULL;
96
97 CHECK(GetProxy(&proxy));
98
99 gboolean rc =
100 org_chromium_UpdateEngineInterface_reset_status(proxy, &error);
101 return rc;
102}
103
104
Darin Petkov58529db2010-08-13 09:19:47 -0700105// If |op| is non-NULL, sets it to the current operation string or an
106// empty string if unable to obtain the current status.
107bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700108 DBusGProxy* proxy;
109 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700110
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700111 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700112
113 gint64 last_checked_time = 0;
114 gdouble progress = 0.0;
115 char* current_op = NULL;
116 char* new_version = NULL;
117 gint64 new_size = 0;
118
119 gboolean rc = org_chromium_UpdateEngineInterface_get_status(
120 proxy,
121 &last_checked_time,
122 &progress,
123 &current_op,
124 &new_version,
125 &new_size,
126 &error);
127 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700128 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700129 }
130 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
131 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
132 last_checked_time,
133 progress,
134 current_op,
135 new_version,
136 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700137 if (op) {
138 *op = current_op ? current_op : "";
139 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700140 return true;
141}
142
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700143// Should never return.
144void WatchForUpdates() {
145 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700146
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700147 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700148
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700149 // Register marshaller
150 dbus_g_object_register_marshaller(
151 update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
152 G_TYPE_NONE,
153 G_TYPE_INT64,
154 G_TYPE_DOUBLE,
155 G_TYPE_STRING,
156 G_TYPE_STRING,
157 G_TYPE_INT64,
158 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700159
160 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700161 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700162 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700163 G_TYPE_INT64,
164 G_TYPE_DOUBLE,
165 G_TYPE_STRING,
166 G_TYPE_STRING,
167 G_TYPE_INT64,
168 G_TYPE_INVALID);
169 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
170 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700171 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700172 G_CALLBACK(StatusUpdateSignalHandler),
173 NULL,
174 NULL);
175 g_main_loop_run(loop);
176 g_main_loop_unref(loop);
177}
178
Darin Petkov58529db2010-08-13 09:19:47 -0700179bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700180 DBusGProxy* proxy;
181 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700182
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700183 CHECK(GetProxy(&proxy));
184
185 gboolean rc =
Darin Petkov5a7f5652010-07-22 21:40:09 -0700186 org_chromium_UpdateEngineInterface_attempt_update(proxy,
187 app_version.c_str(),
188 omaha_url.c_str(),
189 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700190 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700191 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700192 return true;
193}
194
Darin Petkov296889c2010-07-23 16:20:54 -0700195bool RebootIfNeeded() {
196 DBusGProxy* proxy;
197 GError* error = NULL;
198
199 CHECK(GetProxy(&proxy));
200
201 gboolean rc =
202 org_chromium_UpdateEngineInterface_reboot_if_needed(proxy, &error);
203 // Reboot error code doesn't necessarily mean that a reboot
204 // failed. For example, D-Bus may be shutdown before we receive the
205 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700206 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700207 return true;
208}
209
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700210void SetTargetChannel(const string& target_channel) {
Darin Petkov8daa3242010-10-25 13:28:47 -0700211 DBusGProxy* proxy;
212 GError* error = NULL;
213
214 CHECK(GetProxy(&proxy));
215
216 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700217 org_chromium_UpdateEngineInterface_set_channel(proxy,
218 target_channel.c_str(),
219 true, // OK to Powerwash
220 &error);
221 CHECK_EQ(rc, true) << "Error setting the channel: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700222 << GetAndFreeGError(&error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700223 LOG(INFO) << "Channel permanently set to: " << target_channel;
Darin Petkov8daa3242010-10-25 13:28:47 -0700224}
225
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700226string GetChannel(bool get_current_channel) {
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900227 DBusGProxy* proxy;
228 GError* error = NULL;
229
230 CHECK(GetProxy(&proxy));
231
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700232 char* channel = NULL;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900233 gboolean rc =
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700234 org_chromium_UpdateEngineInterface_get_channel(proxy,
235 get_current_channel,
236 &channel,
237 &error);
238 CHECK_EQ(rc, true) << "Error getting the channel: "
239 << GetAndFreeGError(&error);
240 string output = channel;
241 g_free(channel);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900242 return output;
243}
244
Darin Petkov58529db2010-08-13 09:19:47 -0700245static gboolean CompleteUpdateSource(gpointer data) {
246 string current_op;
247 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
248 LOG(ERROR) << "Update failed.";
249 exit(1);
250 }
251 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
252 LOG(INFO) << "Update succeeded -- reboot needed.";
253 exit(0);
254 }
255 return TRUE;
256}
257
258// This is similar to watching for updates but rather than registering
259// a signal watch, activelly poll the daemon just in case it stops
260// sending notifications.
261void CompleteUpdate() {
262 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
263 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
264 g_main_loop_run(loop);
265 g_main_loop_unref(loop);
266}
267
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700268} // namespace {}
269
270int main(int argc, char** argv) {
271 // Boilerplate init commands.
272 g_type_init();
273 g_thread_init(NULL);
274 dbus_g_thread_init();
275 chromeos_update_engine::Subprocess::Init();
276 google::ParseCommandLineFlags(&argc, &argv, true);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700277
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700278 // Update the status if requested.
279 if (FLAGS_reset_status) {
280 LOG(INFO) << "Setting Update Engine status to idle ...";
281 if (!ResetStatus()) {
282 LOG(ERROR) << "ResetStatus failed.";
283 return 1;
284 }
285
Gilad Arnold50c60632013-01-25 10:27:19 -0800286 LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n"
287 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} "
288 "| sed 's/^[^0-9]*//')-1)) $D;)";
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700289 return 0;
290 }
291
292
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700293 if (FLAGS_status) {
294 LOG(INFO) << "Querying Update Engine status...";
Darin Petkov58529db2010-08-13 09:19:47 -0700295 if (!GetStatus(NULL)) {
296 LOG(FATAL) << "GetStatus failed.";
297 return 1;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700298 }
299 return 0;
300 }
Darin Petkov58529db2010-08-13 09:19:47 -0700301
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700302 // First, update the target channel if requested.
303 if (!FLAGS_channel.empty())
304 SetTargetChannel(FLAGS_channel);
Darin Petkov8daa3242010-10-25 13:28:47 -0700305
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700306 // Show the current and target channels if requested.
307 if (FLAGS_show_channel) {
308 string current_channel = GetChannel(true);
309 LOG(INFO) << "Current Channel: " << current_channel;
310
311 string target_channel = GetChannel(false);
312 if (!target_channel.empty())
313 LOG(INFO) << "Target Channel (pending update): " << target_channel;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900314 }
315
Darin Petkov58529db2010-08-13 09:19:47 -0700316 // Initiate an update check, if necessary.
317 if (FLAGS_check_for_update ||
318 FLAGS_update ||
319 !FLAGS_app_version.empty() ||
320 !FLAGS_omaha_url.empty()) {
Darin Petkov296889c2010-07-23 16:20:54 -0700321 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Darin Petkov58529db2010-08-13 09:19:47 -0700322 string app_version = FLAGS_app_version;
323 if (FLAGS_update && app_version.empty()) {
324 app_version = "ForcedUpdate";
325 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700326 }
Darin Petkov58529db2010-08-13 09:19:47 -0700327 LOG(INFO) << "Initiating update check and install.";
328 CHECK(CheckForUpdates(app_version, FLAGS_omaha_url))
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700329 << "Update check/initiate update failed.";
Darin Petkov58529db2010-08-13 09:19:47 -0700330
331 // Wait for an update to complete.
332 if (FLAGS_update) {
Darin Petkov9d911fa2010-08-19 09:36:08 -0700333 LOG(INFO) << "Waiting for update to complete.";
Darin Petkov58529db2010-08-13 09:19:47 -0700334 CompleteUpdate(); // Should never return.
335 return 1;
336 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700337 return 0;
338 }
Darin Petkov58529db2010-08-13 09:19:47 -0700339
340 // Start watching for updates.
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700341 if (FLAGS_watch_for_updates) {
Darin Petkov296889c2010-07-23 16:20:54 -0700342 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700343 LOG(INFO) << "Watching for status updates.";
344 WatchForUpdates(); // Should never return.
345 return 1;
346 }
Darin Petkov58529db2010-08-13 09:19:47 -0700347
Darin Petkov296889c2010-07-23 16:20:54 -0700348 if (FLAGS_reboot) {
349 LOG(INFO) << "Requesting a reboot...";
350 CHECK(RebootIfNeeded());
351 return 0;
352 }
Andrew de los Reyesada42202010-07-15 22:23:20 -0700353
Darin Petkov8daa3242010-10-25 13:28:47 -0700354 LOG(INFO) << "Done.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700355 return 0;
356}