blob: f3b5eeabd100a83e505ea50a49a44ef660259c1b [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.");
Satoru Takabayashi583667b2010-10-27 13:09:57 +090029DEFINE_bool(show_track, false, "Show the update track.");
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.");
Darin Petkov8daa3242010-10-25 13:28:47 -070032DEFINE_string(track, "", "Permanently change the update track.");
Darin Petkov58529db2010-08-13 09:19:47 -070033DEFINE_bool(update, false, "Forces an update and waits for its completion. "
34 "Exit status is 0 if the update succeeded, and 1 otherwise.");
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070035DEFINE_bool(watch_for_updates, false,
36 "Listen for status updates and print them to the screen.");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070037
38namespace {
39
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070040bool GetProxy(DBusGProxy** out_proxy) {
41 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070042 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070043 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070044 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070045 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070046
47 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Richard Barnetted7936062013-01-18 13:38:51 -080048 if (bus == NULL) {
49 LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error);
50 exit(1);
51 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070052 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070053 if (i > 0) {
54 LOG(INFO) << "Retrying to get dbus proxy. Try "
55 << (i + 1) << "/" << kTries;
Gilad Arnold8e3f1262013-01-08 14:59:54 -080056 g_usleep(kRetrySeconds * G_USEC_PER_SEC);
Darin Petkova0b9e772011-10-06 05:05:56 -070057 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070058 proxy = dbus_g_proxy_new_for_name_owner(bus,
59 kUpdateEngineServiceName,
60 kUpdateEngineServicePath,
61 kUpdateEngineServiceInterface,
62 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070063 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
64 << kUpdateEngineServiceName << ": "
65 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070066 }
Richard Barnetted7936062013-01-18 13:38:51 -080067 if (proxy == NULL) {
68 LOG(ERROR) << "Giving up -- unable to get dbus proxy for "
69 << kUpdateEngineServiceName;
70 exit(1);
71 }
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070072 *out_proxy = proxy;
73 return true;
74}
75
76static void StatusUpdateSignalHandler(DBusGProxy* proxy,
77 int64_t last_checked_time,
78 double progress,
79 gchar* current_operation,
80 gchar* new_version,
81 int64_t new_size,
82 void* user_data) {
83 LOG(INFO) << "Got status update:";
84 LOG(INFO) << " last_checked_time: " << last_checked_time;
85 LOG(INFO) << " progress: " << progress;
86 LOG(INFO) << " current_operation: " << current_operation;
87 LOG(INFO) << " new_version: " << new_version;
88 LOG(INFO) << " new_size: " << new_size;
89}
90
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -070091bool ResetStatus() {
92 DBusGProxy* proxy;
93 GError* error = NULL;
94
95 CHECK(GetProxy(&proxy));
96
97 gboolean rc =
98 org_chromium_UpdateEngineInterface_reset_status(proxy, &error);
99 return rc;
100}
101
102
Darin Petkov58529db2010-08-13 09:19:47 -0700103// If |op| is non-NULL, sets it to the current operation string or an
104// empty string if unable to obtain the current status.
105bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700106 DBusGProxy* proxy;
107 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700108
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700109 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700110
111 gint64 last_checked_time = 0;
112 gdouble progress = 0.0;
113 char* current_op = NULL;
114 char* new_version = NULL;
115 gint64 new_size = 0;
116
117 gboolean rc = org_chromium_UpdateEngineInterface_get_status(
118 proxy,
119 &last_checked_time,
120 &progress,
121 &current_op,
122 &new_version,
123 &new_size,
124 &error);
125 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700126 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700127 }
128 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
129 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
130 last_checked_time,
131 progress,
132 current_op,
133 new_version,
134 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700135 if (op) {
136 *op = current_op ? current_op : "";
137 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700138 return true;
139}
140
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700141// Should never return.
142void WatchForUpdates() {
143 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700144
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700145 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700146
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700147 // Register marshaller
148 dbus_g_object_register_marshaller(
149 update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
150 G_TYPE_NONE,
151 G_TYPE_INT64,
152 G_TYPE_DOUBLE,
153 G_TYPE_STRING,
154 G_TYPE_STRING,
155 G_TYPE_INT64,
156 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700157
158 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700159 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700160 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700161 G_TYPE_INT64,
162 G_TYPE_DOUBLE,
163 G_TYPE_STRING,
164 G_TYPE_STRING,
165 G_TYPE_INT64,
166 G_TYPE_INVALID);
167 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
168 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700169 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700170 G_CALLBACK(StatusUpdateSignalHandler),
171 NULL,
172 NULL);
173 g_main_loop_run(loop);
174 g_main_loop_unref(loop);
175}
176
Darin Petkov58529db2010-08-13 09:19:47 -0700177bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700178 DBusGProxy* proxy;
179 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700180
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700181 CHECK(GetProxy(&proxy));
182
183 gboolean rc =
Darin Petkov5a7f5652010-07-22 21:40:09 -0700184 org_chromium_UpdateEngineInterface_attempt_update(proxy,
185 app_version.c_str(),
186 omaha_url.c_str(),
187 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700188 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700189 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700190 return true;
191}
192
Darin Petkov296889c2010-07-23 16:20:54 -0700193bool RebootIfNeeded() {
194 DBusGProxy* proxy;
195 GError* error = NULL;
196
197 CHECK(GetProxy(&proxy));
198
199 gboolean rc =
200 org_chromium_UpdateEngineInterface_reboot_if_needed(proxy, &error);
201 // Reboot error code doesn't necessarily mean that a reboot
202 // failed. For example, D-Bus may be shutdown before we receive the
203 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700204 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700205 return true;
206}
207
Darin Petkov8daa3242010-10-25 13:28:47 -0700208void SetTrack(const string& track) {
209 DBusGProxy* proxy;
210 GError* error = NULL;
211
212 CHECK(GetProxy(&proxy));
213
214 gboolean rc =
215 org_chromium_UpdateEngineInterface_set_track(proxy,
216 track.c_str(),
217 &error);
218 CHECK_EQ(rc, true) << "Error setting the track: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700219 << GetAndFreeGError(&error);
Darin Petkov49d91322010-10-25 16:34:58 -0700220 LOG(INFO) << "Track permanently set to: " << track;
Darin Petkov8daa3242010-10-25 13:28:47 -0700221}
222
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900223string GetTrack() {
224 DBusGProxy* proxy;
225 GError* error = NULL;
226
227 CHECK(GetProxy(&proxy));
228
229 char* track = NULL;
230 gboolean rc =
231 org_chromium_UpdateEngineInterface_get_track(proxy,
232 &track,
233 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -0700234 CHECK_EQ(rc, true) << "Error getting the track: " << GetAndFreeGError(&error);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900235 string output = track;
236 g_free(track);
237 return output;
238}
239
Darin Petkov58529db2010-08-13 09:19:47 -0700240static gboolean CompleteUpdateSource(gpointer data) {
241 string current_op;
242 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
243 LOG(ERROR) << "Update failed.";
244 exit(1);
245 }
246 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
247 LOG(INFO) << "Update succeeded -- reboot needed.";
248 exit(0);
249 }
250 return TRUE;
251}
252
253// This is similar to watching for updates but rather than registering
254// a signal watch, activelly poll the daemon just in case it stops
255// sending notifications.
256void CompleteUpdate() {
257 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
258 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
259 g_main_loop_run(loop);
260 g_main_loop_unref(loop);
261}
262
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700263} // namespace {}
264
265int main(int argc, char** argv) {
266 // Boilerplate init commands.
267 g_type_init();
268 g_thread_init(NULL);
269 dbus_g_thread_init();
270 chromeos_update_engine::Subprocess::Init();
271 google::ParseCommandLineFlags(&argc, &argv, true);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700272
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700273 // Update the status if requested.
274 if (FLAGS_reset_status) {
275 LOG(INFO) << "Setting Update Engine status to idle ...";
276 if (!ResetStatus()) {
277 LOG(ERROR) << "ResetStatus failed.";
278 return 1;
279 }
280
Gilad Arnold50c60632013-01-25 10:27:19 -0800281 LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n"
282 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} "
283 "| sed 's/^[^0-9]*//')-1)) $D;)";
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700284 return 0;
285 }
286
287
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700288 if (FLAGS_status) {
289 LOG(INFO) << "Querying Update Engine status...";
Darin Petkov58529db2010-08-13 09:19:47 -0700290 if (!GetStatus(NULL)) {
291 LOG(FATAL) << "GetStatus failed.";
292 return 1;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700293 }
294 return 0;
295 }
Darin Petkov58529db2010-08-13 09:19:47 -0700296
Darin Petkov8daa3242010-10-25 13:28:47 -0700297 // First, update the track if requested.
298 if (!FLAGS_track.empty()) {
299 SetTrack(FLAGS_track);
300 }
301
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900302 // Show the track if requested.
303 if (FLAGS_show_track) {
304 LOG(INFO) << "Track: " << GetTrack();
305 }
306
Darin Petkov58529db2010-08-13 09:19:47 -0700307 // Initiate an update check, if necessary.
308 if (FLAGS_check_for_update ||
309 FLAGS_update ||
310 !FLAGS_app_version.empty() ||
311 !FLAGS_omaha_url.empty()) {
Darin Petkov296889c2010-07-23 16:20:54 -0700312 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Darin Petkov58529db2010-08-13 09:19:47 -0700313 string app_version = FLAGS_app_version;
314 if (FLAGS_update && app_version.empty()) {
315 app_version = "ForcedUpdate";
316 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700317 }
Darin Petkov58529db2010-08-13 09:19:47 -0700318 LOG(INFO) << "Initiating update check and install.";
319 CHECK(CheckForUpdates(app_version, FLAGS_omaha_url))
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700320 << "Update check/initiate update failed.";
Darin Petkov58529db2010-08-13 09:19:47 -0700321
322 // Wait for an update to complete.
323 if (FLAGS_update) {
Darin Petkov9d911fa2010-08-19 09:36:08 -0700324 LOG(INFO) << "Waiting for update to complete.";
Darin Petkov58529db2010-08-13 09:19:47 -0700325 CompleteUpdate(); // Should never return.
326 return 1;
327 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700328 return 0;
329 }
Darin Petkov58529db2010-08-13 09:19:47 -0700330
331 // Start watching for updates.
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700332 if (FLAGS_watch_for_updates) {
Darin Petkov296889c2010-07-23 16:20:54 -0700333 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700334 LOG(INFO) << "Watching for status updates.";
335 WatchForUpdates(); // Should never return.
336 return 1;
337 }
Darin Petkov58529db2010-08-13 09:19:47 -0700338
Darin Petkov296889c2010-07-23 16:20:54 -0700339 if (FLAGS_reboot) {
340 LOG(INFO) << "Requesting a reboot...";
341 CHECK(RebootIfNeeded());
342 return 0;
343 }
Andrew de los Reyesada42202010-07-15 22:23:20 -0700344
Darin Petkov8daa3242010-10-25 13:28:47 -0700345 LOG(INFO) << "Done.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700346 return 0;
347}