blob: db14fb6b74dfe181ee2eac80520058c1f285e00c [file] [log] [blame]
Darin Petkova0b9e772011-10-06 05:05:56 -07001// Copyright (c) 2011 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.");
Darin Petkov8daa3242010-10-25 13:28:47 -070031DEFINE_string(track, "", "Permanently change the update track.");
Darin Petkov58529db2010-08-13 09:19:47 -070032DEFINE_bool(update, false, "Forces an update and waits for its completion. "
33 "Exit status is 0 if the update succeeded, and 1 otherwise.");
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070034DEFINE_bool(watch_for_updates, false,
35 "Listen for status updates and print them to the screen.");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070036
37namespace {
38
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070039bool GetProxy(DBusGProxy** out_proxy) {
40 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070041 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070042 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070043 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070044 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070045
46 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070047 LOG_IF(FATAL, !bus) << "Failed to get bus: " << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070048 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070049 if (i > 0) {
50 LOG(INFO) << "Retrying to get dbus proxy. Try "
51 << (i + 1) << "/" << kTries;
52 sleep(kRetrySeconds);
53 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070054 proxy = dbus_g_proxy_new_for_name_owner(bus,
55 kUpdateEngineServiceName,
56 kUpdateEngineServicePath,
57 kUpdateEngineServiceInterface,
58 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070059 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
60 << kUpdateEngineServiceName << ": "
61 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070062 }
Darin Petkova0b9e772011-10-06 05:05:56 -070063 LOG_IF(FATAL, !proxy) << "Giving up -- unable to get dbus proxy for "
64 << kUpdateEngineServiceName;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070065 *out_proxy = proxy;
66 return true;
67}
68
69static void StatusUpdateSignalHandler(DBusGProxy* proxy,
70 int64_t last_checked_time,
71 double progress,
72 gchar* current_operation,
73 gchar* new_version,
74 int64_t new_size,
75 void* user_data) {
76 LOG(INFO) << "Got status update:";
77 LOG(INFO) << " last_checked_time: " << last_checked_time;
78 LOG(INFO) << " progress: " << progress;
79 LOG(INFO) << " current_operation: " << current_operation;
80 LOG(INFO) << " new_version: " << new_version;
81 LOG(INFO) << " new_size: " << new_size;
82}
83
Darin Petkov58529db2010-08-13 09:19:47 -070084// If |op| is non-NULL, sets it to the current operation string or an
85// empty string if unable to obtain the current status.
86bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070087 DBusGProxy* proxy;
88 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -070089
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070090 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070091
92 gint64 last_checked_time = 0;
93 gdouble progress = 0.0;
94 char* current_op = NULL;
95 char* new_version = NULL;
96 gint64 new_size = 0;
97
98 gboolean rc = org_chromium_UpdateEngineInterface_get_status(
99 proxy,
100 &last_checked_time,
101 &progress,
102 &current_op,
103 &new_version,
104 &new_size,
105 &error);
106 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700107 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700108 }
109 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
110 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
111 last_checked_time,
112 progress,
113 current_op,
114 new_version,
115 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700116 if (op) {
117 *op = current_op ? current_op : "";
118 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700119 return true;
120}
121
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700122// Should never return.
123void WatchForUpdates() {
124 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700125
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700126 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700127
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700128 // Register marshaller
129 dbus_g_object_register_marshaller(
130 update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
131 G_TYPE_NONE,
132 G_TYPE_INT64,
133 G_TYPE_DOUBLE,
134 G_TYPE_STRING,
135 G_TYPE_STRING,
136 G_TYPE_INT64,
137 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700138
139 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700140 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700141 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700142 G_TYPE_INT64,
143 G_TYPE_DOUBLE,
144 G_TYPE_STRING,
145 G_TYPE_STRING,
146 G_TYPE_INT64,
147 G_TYPE_INVALID);
148 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
149 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700150 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700151 G_CALLBACK(StatusUpdateSignalHandler),
152 NULL,
153 NULL);
154 g_main_loop_run(loop);
155 g_main_loop_unref(loop);
156}
157
Darin Petkov58529db2010-08-13 09:19:47 -0700158bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700159 DBusGProxy* proxy;
160 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700161
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700162 CHECK(GetProxy(&proxy));
163
164 gboolean rc =
Darin Petkov5a7f5652010-07-22 21:40:09 -0700165 org_chromium_UpdateEngineInterface_attempt_update(proxy,
166 app_version.c_str(),
167 omaha_url.c_str(),
168 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700169 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700170 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700171 return true;
172}
173
Darin Petkov296889c2010-07-23 16:20:54 -0700174bool RebootIfNeeded() {
175 DBusGProxy* proxy;
176 GError* error = NULL;
177
178 CHECK(GetProxy(&proxy));
179
180 gboolean rc =
181 org_chromium_UpdateEngineInterface_reboot_if_needed(proxy, &error);
182 // Reboot error code doesn't necessarily mean that a reboot
183 // failed. For example, D-Bus may be shutdown before we receive the
184 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700185 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700186 return true;
187}
188
Darin Petkov8daa3242010-10-25 13:28:47 -0700189void SetTrack(const string& track) {
190 DBusGProxy* proxy;
191 GError* error = NULL;
192
193 CHECK(GetProxy(&proxy));
194
195 gboolean rc =
196 org_chromium_UpdateEngineInterface_set_track(proxy,
197 track.c_str(),
198 &error);
199 CHECK_EQ(rc, true) << "Error setting the track: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700200 << GetAndFreeGError(&error);
Darin Petkov49d91322010-10-25 16:34:58 -0700201 LOG(INFO) << "Track permanently set to: " << track;
Darin Petkov8daa3242010-10-25 13:28:47 -0700202}
203
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900204string GetTrack() {
205 DBusGProxy* proxy;
206 GError* error = NULL;
207
208 CHECK(GetProxy(&proxy));
209
210 char* track = NULL;
211 gboolean rc =
212 org_chromium_UpdateEngineInterface_get_track(proxy,
213 &track,
214 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -0700215 CHECK_EQ(rc, true) << "Error getting the track: " << GetAndFreeGError(&error);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900216 string output = track;
217 g_free(track);
218 return output;
219}
220
Darin Petkov58529db2010-08-13 09:19:47 -0700221static gboolean CompleteUpdateSource(gpointer data) {
222 string current_op;
223 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
224 LOG(ERROR) << "Update failed.";
225 exit(1);
226 }
227 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
228 LOG(INFO) << "Update succeeded -- reboot needed.";
229 exit(0);
230 }
231 return TRUE;
232}
233
234// This is similar to watching for updates but rather than registering
235// a signal watch, activelly poll the daemon just in case it stops
236// sending notifications.
237void CompleteUpdate() {
238 GMainLoop* loop = g_main_loop_new (NULL, TRUE);
239 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
240 g_main_loop_run(loop);
241 g_main_loop_unref(loop);
242}
243
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700244} // namespace {}
245
246int main(int argc, char** argv) {
247 // Boilerplate init commands.
248 g_type_init();
249 g_thread_init(NULL);
250 dbus_g_thread_init();
251 chromeos_update_engine::Subprocess::Init();
252 google::ParseCommandLineFlags(&argc, &argv, true);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700253
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700254 if (FLAGS_status) {
255 LOG(INFO) << "Querying Update Engine status...";
Darin Petkov58529db2010-08-13 09:19:47 -0700256 if (!GetStatus(NULL)) {
257 LOG(FATAL) << "GetStatus failed.";
258 return 1;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700259 }
260 return 0;
261 }
Darin Petkov58529db2010-08-13 09:19:47 -0700262
Darin Petkov8daa3242010-10-25 13:28:47 -0700263 // First, update the track if requested.
264 if (!FLAGS_track.empty()) {
265 SetTrack(FLAGS_track);
266 }
267
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900268 // Show the track if requested.
269 if (FLAGS_show_track) {
270 LOG(INFO) << "Track: " << GetTrack();
271 }
272
Darin Petkov58529db2010-08-13 09:19:47 -0700273 // Initiate an update check, if necessary.
274 if (FLAGS_check_for_update ||
275 FLAGS_update ||
276 !FLAGS_app_version.empty() ||
277 !FLAGS_omaha_url.empty()) {
Darin Petkov296889c2010-07-23 16:20:54 -0700278 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Darin Petkov58529db2010-08-13 09:19:47 -0700279 string app_version = FLAGS_app_version;
280 if (FLAGS_update && app_version.empty()) {
281 app_version = "ForcedUpdate";
282 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700283 }
Darin Petkov58529db2010-08-13 09:19:47 -0700284 LOG(INFO) << "Initiating update check and install.";
285 CHECK(CheckForUpdates(app_version, FLAGS_omaha_url))
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700286 << "Update check/initiate update failed.";
Darin Petkov58529db2010-08-13 09:19:47 -0700287
288 // Wait for an update to complete.
289 if (FLAGS_update) {
Darin Petkov9d911fa2010-08-19 09:36:08 -0700290 LOG(INFO) << "Waiting for update to complete.";
Darin Petkov58529db2010-08-13 09:19:47 -0700291 CompleteUpdate(); // Should never return.
292 return 1;
293 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700294 return 0;
295 }
Darin Petkov58529db2010-08-13 09:19:47 -0700296
297 // Start watching for updates.
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700298 if (FLAGS_watch_for_updates) {
Darin Petkov296889c2010-07-23 16:20:54 -0700299 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700300 LOG(INFO) << "Watching for status updates.";
301 WatchForUpdates(); // Should never return.
302 return 1;
303 }
Darin Petkov58529db2010-08-13 09:19:47 -0700304
Darin Petkov296889c2010-07-23 16:20:54 -0700305 if (FLAGS_reboot) {
306 LOG(INFO) << "Requesting a reboot...";
307 CHECK(RebootIfNeeded());
308 return 0;
309 }
Andrew de los Reyesada42202010-07-15 22:23:20 -0700310
Darin Petkov8daa3242010-10-25 13:28:47 -0700311 LOG(INFO) << "Done.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700312 return 0;
313}