blob: 516895a85ad8b6a91a4b10877532e825cdeb031c [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
David Zeuthen9d73a722014-04-04 14:52:46 -07007#include <chromeos/dbus/service_constants.h>
Ben Chan46bf5c82013-06-24 11:17:41 -07008#include <dbus/dbus.h>
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -07009#include <gflags/gflags.h>
10#include <glib.h>
11
12#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;
David Zeuthen75a4c3e2013-09-06 11:36:59 -070023using chromeos_update_engine::AttemptUpdateFlags;
24using chromeos_update_engine::kAttemptUpdateFlagNonInteractive;
Darin Petkova0b9e772011-10-06 05:05:56 -070025using chromeos_update_engine::utils::GetAndFreeGError;
Darin Petkov5a7f5652010-07-22 21:40:09 -070026using std::string;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070027
Darin Petkov296889c2010-07-23 16:20:54 -070028DEFINE_string(app_version, "", "Force the current app version.");
Jay Srinivasanae4697c2013-03-18 17:08:08 -070029DEFINE_string(channel, "",
Chris Sosa192449e2013-10-28 14:16:19 -070030 "Set the target channel. The device will be powerwashed if the "
31 "target channel is more stable than the current channel unless "
32 "--nopowerwash is specified.");
Chris Sosad317e402013-06-12 13:47:09 -070033DEFINE_bool(check_for_update, false, "Initiate check for updates.");
Chris Sosa192449e2013-10-28 14:16:19 -070034DEFINE_bool(follow, false, "Wait for any update operations to complete."
35 "Exit status is 0 if the update succeeded, and 1 otherwise.");
36DEFINE_bool(interactive, true, "Mark the update request as interactive.");
Chris Sosad317e402013-06-12 13:47:09 -070037DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
Chris Sosa192449e2013-10-28 14:16:19 -070038DEFINE_string(p2p_update, "",
39 "Enables (\"yes\") or disables (\"no\") the peer-to-peer update "
40 "sharing.");
41DEFINE_bool(powerwash, true, "When performing rollback or channel change, "
42 "do a powerwash or allow it respectively.");
Chris Sosad317e402013-06-12 13:47:09 -070043DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
David Zeuthen9d73a722014-04-04 14:52:46 -070044DEFINE_bool(is_reboot_needed, false, "Exit status 0 if reboot is needed, "
45 "2 if reboot is not needed or 1 if an error occurred.");
46DEFINE_bool(block_until_reboot_is_needed, false, "Blocks until reboot is "
47 "needed. Returns non-zero exit status if an error occurred.");
Chris Sosad317e402013-06-12 13:47:09 -070048DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
49DEFINE_bool(rollback, false, "Perform a rollback to the previous partition.");
Alex Vakulenko59e253e2014-02-24 10:40:21 -080050DEFINE_bool(can_rollback, false, "Shows whether rollback partition "
51 "is available.");
Chris Sosad317e402013-06-12 13:47:09 -070052DEFINE_bool(show_channel, false, "Show the current and target channels.");
Chris Sosa192449e2013-10-28 14:16:19 -070053DEFINE_bool(show_p2p_update, false,
54 "Show the current setting for peer-to-peer update sharing.");
Alex Deymof4867c42013-06-28 14:41:39 -070055DEFINE_bool(show_update_over_cellular, false,
56 "Show the current setting for updates over cellular networks.");
Chris Sosa192449e2013-10-28 14:16:19 -070057DEFINE_bool(status, false, "Print the status to stdout.");
58DEFINE_bool(update, false, "Forces an update and waits for it to complete. "
59 "Implies --follow.");
Alex Deymof4867c42013-06-28 14:41:39 -070060DEFINE_string(update_over_cellular, "",
61 "Enables (\"yes\") or disables (\"no\") the updates over "
62 "cellular networks.");
Chris Sosa192449e2013-10-28 14:16:19 -070063DEFINE_bool(watch_for_updates, false,
64 "Listen for status updates and print them to the screen.");
Alex Vakulenkodea2eac2014-03-14 15:56:59 -070065DEFINE_bool(prev_version, false,
66 "Show the previous OS version used before the update reboot.");
Alex Vakulenko2bddadd2014-03-27 13:23:46 -070067DEFINE_bool(show_kernels, false, "Show the list of kernel patritions and "
68 "whether each of them is bootable or not");
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070069
70namespace {
71
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070072bool GetProxy(DBusGProxy** out_proxy) {
73 DBusGConnection* bus;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070074 DBusGProxy* proxy = NULL;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070075 GError* error = NULL;
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070076 const int kTries = 4;
Darin Petkova0b9e772011-10-06 05:05:56 -070077 const int kRetrySeconds = 10;
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -070078
79 bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
Richard Barnetted7936062013-01-18 13:38:51 -080080 if (bus == NULL) {
81 LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error);
82 exit(1);
83 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070084 for (int i = 0; !proxy && i < kTries; ++i) {
Darin Petkova0b9e772011-10-06 05:05:56 -070085 if (i > 0) {
86 LOG(INFO) << "Retrying to get dbus proxy. Try "
87 << (i + 1) << "/" << kTries;
Gilad Arnold8e3f1262013-01-08 14:59:54 -080088 g_usleep(kRetrySeconds * G_USEC_PER_SEC);
Darin Petkova0b9e772011-10-06 05:05:56 -070089 }
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070090 proxy = dbus_g_proxy_new_for_name_owner(bus,
91 kUpdateEngineServiceName,
92 kUpdateEngineServicePath,
93 kUpdateEngineServiceInterface,
94 &error);
Darin Petkova0b9e772011-10-06 05:05:56 -070095 LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for "
96 << kUpdateEngineServiceName << ": "
97 << GetAndFreeGError(&error);
Andrew de los Reyes68ab6ed2011-08-09 14:46:39 -070098 }
Richard Barnetted7936062013-01-18 13:38:51 -080099 if (proxy == NULL) {
100 LOG(ERROR) << "Giving up -- unable to get dbus proxy for "
101 << kUpdateEngineServiceName;
102 exit(1);
103 }
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700104 *out_proxy = proxy;
105 return true;
106}
107
108static void StatusUpdateSignalHandler(DBusGProxy* proxy,
109 int64_t last_checked_time,
110 double progress,
111 gchar* current_operation,
112 gchar* new_version,
113 int64_t new_size,
114 void* user_data) {
115 LOG(INFO) << "Got status update:";
116 LOG(INFO) << " last_checked_time: " << last_checked_time;
117 LOG(INFO) << " progress: " << progress;
118 LOG(INFO) << " current_operation: " << current_operation;
119 LOG(INFO) << " new_version: " << new_version;
120 LOG(INFO) << " new_size: " << new_size;
121}
122
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700123bool ResetStatus() {
124 DBusGProxy* proxy;
125 GError* error = NULL;
126
127 CHECK(GetProxy(&proxy));
128
Alex Deymo36dc2f32013-08-29 15:45:06 -0700129 gboolean rc = update_engine_client_reset_status(proxy, &error);
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700130 return rc;
131}
132
133
Darin Petkov58529db2010-08-13 09:19:47 -0700134// If |op| is non-NULL, sets it to the current operation string or an
135// empty string if unable to obtain the current status.
136bool GetStatus(string* op) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700137 DBusGProxy* proxy;
138 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700139
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700140 CHECK(GetProxy(&proxy));
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700141
142 gint64 last_checked_time = 0;
143 gdouble progress = 0.0;
144 char* current_op = NULL;
145 char* new_version = NULL;
146 gint64 new_size = 0;
147
Alex Deymo36dc2f32013-08-29 15:45:06 -0700148 gboolean rc = update_engine_client_get_status(proxy,
149 &last_checked_time,
150 &progress,
151 &current_op,
152 &new_version,
153 &new_size,
154 &error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700155 if (rc == FALSE) {
Darin Petkova0b9e772011-10-06 05:05:56 -0700156 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700157 }
158 printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n"
159 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
160 last_checked_time,
161 progress,
162 current_op,
163 new_version,
164 new_size);
Darin Petkov58529db2010-08-13 09:19:47 -0700165 if (op) {
166 *op = current_op ? current_op : "";
167 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700168 return true;
169}
170
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700171// Should never return.
172void WatchForUpdates() {
173 DBusGProxy* proxy;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700174
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700175 CHECK(GetProxy(&proxy));
Andrew de los Reyesada42202010-07-15 22:23:20 -0700176
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700177 // Register marshaller
178 dbus_g_object_register_marshaller(
David Zeuthen7cb12f92014-04-08 10:35:39 -0700179 g_cclosure_marshal_generic,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700180 G_TYPE_NONE,
181 G_TYPE_INT64,
182 G_TYPE_DOUBLE,
183 G_TYPE_STRING,
184 G_TYPE_STRING,
185 G_TYPE_INT64,
186 G_TYPE_INVALID);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700187
188 static const char kStatusUpdate[] = "StatusUpdate";
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700189 dbus_g_proxy_add_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700190 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700191 G_TYPE_INT64,
192 G_TYPE_DOUBLE,
193 G_TYPE_STRING,
194 G_TYPE_STRING,
195 G_TYPE_INT64,
196 G_TYPE_INVALID);
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700197 GMainLoop* loop = g_main_loop_new(NULL, TRUE);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700198 dbus_g_proxy_connect_signal(proxy,
Andrew de los Reyesada42202010-07-15 22:23:20 -0700199 kStatusUpdate,
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700200 G_CALLBACK(StatusUpdateSignalHandler),
201 NULL,
202 NULL);
203 g_main_loop_run(loop);
204 g_main_loop_unref(loop);
205}
206
Chris Sosad317e402013-06-12 13:47:09 -0700207bool Rollback(bool rollback) {
208 DBusGProxy* proxy;
209 GError* error = NULL;
210
211 CHECK(GetProxy(&proxy));
212
Alex Deymo36dc2f32013-08-29 15:45:06 -0700213 gboolean rc = update_engine_client_attempt_rollback(proxy,
214 rollback,
215 &error);
Chris Sosad317e402013-06-12 13:47:09 -0700216 CHECK_EQ(rc, TRUE) << "Error with rollback request: "
217 << GetAndFreeGError(&error);
218 return true;
219}
220
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700221std::string GetRollbackPartition() {
Alex Vakulenko59e253e2014-02-24 10:40:21 -0800222 DBusGProxy* proxy;
223 GError* error = NULL;
224
225 CHECK(GetProxy(&proxy));
226
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700227 char* rollback_partition = nullptr;
228 gboolean rc = update_engine_client_get_rollback_partition(proxy,
229 &rollback_partition,
230 &error);
Alex Vakulenko59e253e2014-02-24 10:40:21 -0800231 CHECK_EQ(rc, TRUE) << "Error while querying rollback partition availabilty: "
232 << GetAndFreeGError(&error);
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700233 std::string partition = rollback_partition;
234 g_free(rollback_partition);
235 return partition;
Alex Vakulenko59e253e2014-02-24 10:40:21 -0800236}
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700237
238std::string GetKernelDevices() {
239 DBusGProxy* proxy;
240 GError* error = nullptr;
241
242 CHECK(GetProxy(&proxy));
243
244 char* kernel_devices = nullptr;
245 gboolean rc = update_engine_client_get_kernel_devices(proxy,
246 &kernel_devices,
247 &error);
248 CHECK_EQ(rc, TRUE) << "Error while getting a list of kernel devices: "
249 << GetAndFreeGError(&error);
250 std::string devices = kernel_devices;
251 g_free(kernel_devices);
252 return devices;
253}
254
Darin Petkov58529db2010-08-13 09:19:47 -0700255bool CheckForUpdates(const string& app_version, const string& omaha_url) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700256 DBusGProxy* proxy;
257 GError* error = NULL;
Andrew de los Reyesada42202010-07-15 22:23:20 -0700258
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700259 CHECK(GetProxy(&proxy));
260
David Zeuthen75a4c3e2013-09-06 11:36:59 -0700261 AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>(
262 FLAGS_interactive ? 0 : kAttemptUpdateFlagNonInteractive);
263 gboolean rc =
264 update_engine_client_attempt_update_with_flags(proxy,
265 app_version.c_str(),
266 omaha_url.c_str(),
267 static_cast<gint>(flags),
268 &error);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700269 CHECK_EQ(rc, TRUE) << "Error checking for update: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700270 << GetAndFreeGError(&error);
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700271 return true;
272}
273
Darin Petkov296889c2010-07-23 16:20:54 -0700274bool RebootIfNeeded() {
275 DBusGProxy* proxy;
276 GError* error = NULL;
277
278 CHECK(GetProxy(&proxy));
279
280 gboolean rc =
Alex Deymo36dc2f32013-08-29 15:45:06 -0700281 update_engine_client_reboot_if_needed(proxy, &error);
Darin Petkov296889c2010-07-23 16:20:54 -0700282 // Reboot error code doesn't necessarily mean that a reboot
283 // failed. For example, D-Bus may be shutdown before we receive the
284 // result.
Darin Petkova0b9e772011-10-06 05:05:56 -0700285 LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error);
Darin Petkov296889c2010-07-23 16:20:54 -0700286 return true;
287}
288
Chris Sosacb7fa882013-07-25 17:02:59 -0700289void SetTargetChannel(const string& target_channel, bool allow_powerwash) {
Darin Petkov8daa3242010-10-25 13:28:47 -0700290 DBusGProxy* proxy;
291 GError* error = NULL;
292
293 CHECK(GetProxy(&proxy));
294
Alex Deymo36dc2f32013-08-29 15:45:06 -0700295 gboolean rc = update_engine_client_set_channel(proxy,
296 target_channel.c_str(),
297 allow_powerwash,
298 &error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700299 CHECK_EQ(rc, true) << "Error setting the channel: "
Darin Petkova0b9e772011-10-06 05:05:56 -0700300 << GetAndFreeGError(&error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700301 LOG(INFO) << "Channel permanently set to: " << target_channel;
Darin Petkov8daa3242010-10-25 13:28:47 -0700302}
303
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700304string GetChannel(bool get_current_channel) {
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900305 DBusGProxy* proxy;
306 GError* error = NULL;
307
308 CHECK(GetProxy(&proxy));
309
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700310 char* channel = NULL;
Alex Deymo36dc2f32013-08-29 15:45:06 -0700311 gboolean rc = update_engine_client_get_channel(proxy,
312 get_current_channel,
313 &channel,
314 &error);
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700315 CHECK_EQ(rc, true) << "Error getting the channel: "
316 << GetAndFreeGError(&error);
317 string output = channel;
318 g_free(channel);
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900319 return output;
320}
321
Alex Deymo5fdf7762013-07-17 20:01:40 -0700322void SetUpdateOverCellularPermission(gboolean allowed) {
Alex Deymof4867c42013-06-28 14:41:39 -0700323 DBusGProxy* proxy;
324 GError* error = NULL;
325
326 CHECK(GetProxy(&proxy));
327
Alex Deymo36dc2f32013-08-29 15:45:06 -0700328 gboolean rc = update_engine_client_set_update_over_cellular_permission(
329 proxy,
330 allowed,
331 &error);
Alex Deymof4867c42013-06-28 14:41:39 -0700332 CHECK_EQ(rc, true) << "Error setting the update over cellular setting: "
333 << GetAndFreeGError(&error);
Alex Deymof4867c42013-06-28 14:41:39 -0700334}
335
336bool GetUpdateOverCellularPermission() {
337 DBusGProxy* proxy;
338 GError* error = NULL;
339
340 CHECK(GetProxy(&proxy));
341
342 gboolean allowed;
Alex Deymo36dc2f32013-08-29 15:45:06 -0700343 gboolean rc = update_engine_client_get_update_over_cellular_permission(
344 proxy,
345 &allowed,
346 &error);
Alex Deymof4867c42013-06-28 14:41:39 -0700347 CHECK_EQ(rc, true) << "Error getting the update over cellular setting: "
348 << GetAndFreeGError(&error);
349 return allowed;
350}
351
Alex Deymo5fdf7762013-07-17 20:01:40 -0700352void SetP2PUpdatePermission(gboolean enabled) {
353 DBusGProxy* proxy;
354 GError* error = NULL;
355
356 CHECK(GetProxy(&proxy));
357
Alex Deymo36dc2f32013-08-29 15:45:06 -0700358 gboolean rc = update_engine_client_set_p2p_update_permission(
359 proxy,
360 enabled,
361 &error);
Alex Deymo5fdf7762013-07-17 20:01:40 -0700362 CHECK_EQ(rc, true) << "Error setting the peer-to-peer update setting: "
363 << GetAndFreeGError(&error);
364}
365
366bool GetP2PUpdatePermission() {
367 DBusGProxy* proxy;
368 GError* error = NULL;
369
370 CHECK(GetProxy(&proxy));
371
372 gboolean enabled;
Alex Deymo36dc2f32013-08-29 15:45:06 -0700373 gboolean rc = update_engine_client_get_p2p_update_permission(
374 proxy,
375 &enabled,
376 &error);
Alex Deymo5fdf7762013-07-17 20:01:40 -0700377 CHECK_EQ(rc, true) << "Error getting the peer-to-peer update setting: "
378 << GetAndFreeGError(&error);
379 return enabled;
380}
381
Darin Petkov58529db2010-08-13 09:19:47 -0700382static gboolean CompleteUpdateSource(gpointer data) {
383 string current_op;
384 if (!GetStatus(&current_op) || current_op == "UPDATE_STATUS_IDLE") {
385 LOG(ERROR) << "Update failed.";
386 exit(1);
387 }
388 if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") {
389 LOG(INFO) << "Update succeeded -- reboot needed.";
390 exit(0);
391 }
392 return TRUE;
393}
394
395// This is similar to watching for updates but rather than registering
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700396// a signal watch, actively poll the daemon just in case it stops
Darin Petkov58529db2010-08-13 09:19:47 -0700397// sending notifications.
398void CompleteUpdate() {
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700399 GMainLoop* loop = g_main_loop_new(NULL, TRUE);
Darin Petkov58529db2010-08-13 09:19:47 -0700400 g_timeout_add_seconds(5, CompleteUpdateSource, NULL);
401 g_main_loop_run(loop);
402 g_main_loop_unref(loop);
403}
404
Alex Vakulenkodea2eac2014-03-14 15:56:59 -0700405void ShowPrevVersion() {
406 DBusGProxy* proxy;
407 GError* error = nullptr;
408
409 CHECK(GetProxy(&proxy));
410
411 char* prev_version = nullptr;
412
413 gboolean rc = update_engine_client_get_prev_version(proxy,
414 &prev_version,
415 &error);
416 if (!rc) {
417 LOG(ERROR) << "Error getting previous version: "
418 << GetAndFreeGError(&error);
419 } else {
420 LOG(INFO) << "Previous version = " << prev_version;
421 g_free(prev_version);
422 }
423}
424
David Zeuthen9d73a722014-04-04 14:52:46 -0700425bool CheckIfRebootIsNeeded(DBusGProxy *proxy, bool *out_reboot_needed) {
426 gint64 last_checked_time = 0;
427 gdouble progress = 0.0;
428 char* current_op = nullptr;
429 char* new_version = nullptr;
430 gint64 new_size = 0;
431 GError* error = nullptr;
432
433 if (!update_engine_client_get_status(proxy,
434 &last_checked_time,
435 &progress,
436 &current_op,
437 &new_version,
438 &new_size,
439 &error)) {
440 LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error);
441 return false;
442 }
443 *out_reboot_needed =
444 (g_strcmp0(current_op,
445 update_engine::kUpdateStatusUpdatedNeedReboot) == 0);
446 g_free(current_op);
447 g_free(new_version);
448 return true;
449}
450
451// Determines if reboot is needed. The result is returned in
452// |out_reboot_needed|. Returns true if the check succeeded, false
453// otherwise.
454bool IsRebootNeeded(bool *out_reboot_needed) {
455 DBusGProxy* proxy = nullptr;
456 CHECK(GetProxy(&proxy));
457 bool ret = CheckIfRebootIsNeeded(proxy, out_reboot_needed);
458 g_object_unref(proxy);
459 return ret;
460}
461
462static void OnBlockUntilRebootStatusCallback(
463 DBusGProxy* proxy,
464 int64_t last_checked_time,
465 double progress,
466 const gchar* current_operation,
467 const gchar* new_version,
468 int64_t new_size,
469 void* user_data) {
470 GMainLoop *loop = reinterpret_cast<GMainLoop*>(user_data);
471 if (g_strcmp0(current_operation,
472 update_engine::kUpdateStatusUpdatedNeedReboot) == 0) {
473 g_main_loop_quit(loop);
474 }
475}
476
477bool CheckRebootNeeded(DBusGProxy *proxy, GMainLoop *loop) {
478 bool reboot_needed;
479 if (!CheckIfRebootIsNeeded(proxy, &reboot_needed))
480 return false;
481 if (reboot_needed)
482 return true;
483 // This will block until OnBlockUntilRebootStatusCallback() calls
484 // g_main_loop_quit().
485 g_main_loop_run(loop);
486 return true;
487}
488
489// Blocks until a reboot is needed. Returns true if waiting succeeded,
490// false if an error occurred.
491bool BlockUntilRebootIsNeeded() {
492 // The basic idea is to get a proxy, listen to signals and only then
493 // check the status. If no reboot is needed, just sit and wait for
494 // the StatusUpdate signal to convey that a reboot is pending.
495 DBusGProxy* proxy = nullptr;
496 CHECK(GetProxy(&proxy));
497 dbus_g_object_register_marshaller(
498 g_cclosure_marshal_generic,
499 G_TYPE_NONE,
500 G_TYPE_INT64,
501 G_TYPE_DOUBLE,
502 G_TYPE_STRING,
503 G_TYPE_STRING,
504 G_TYPE_INT64,
505 G_TYPE_INVALID);
506 dbus_g_proxy_add_signal(proxy,
507 update_engine::kStatusUpdate, // Signal name.
508 G_TYPE_INT64,
509 G_TYPE_DOUBLE,
510 G_TYPE_STRING,
511 G_TYPE_STRING,
512 G_TYPE_INT64,
513 G_TYPE_INVALID);
514 GMainLoop* loop = g_main_loop_new(nullptr, TRUE);
515 dbus_g_proxy_connect_signal(proxy,
516 update_engine::kStatusUpdate,
517 G_CALLBACK(OnBlockUntilRebootStatusCallback),
518 loop,
519 nullptr); // free_data_func.
520
521 bool ret = CheckRebootNeeded(proxy, loop);
522
523 dbus_g_proxy_disconnect_signal(proxy,
524 update_engine::kStatusUpdate,
525 G_CALLBACK(OnBlockUntilRebootStatusCallback),
526 loop);
527 g_main_loop_unref(loop);
528 g_object_unref(proxy);
529 return ret;
530}
531
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700532} // namespace
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700533
534int main(int argc, char** argv) {
535 // Boilerplate init commands.
536 g_type_init();
Ben Chan46bf5c82013-06-24 11:17:41 -0700537 dbus_threads_init_default();
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700538 chromeos_update_engine::Subprocess::Init();
539 google::ParseCommandLineFlags(&argc, &argv, true);
Andrew de los Reyesada42202010-07-15 22:23:20 -0700540
Jay Srinivasanc1ba09a2012-08-14 14:15:57 -0700541 // Update the status if requested.
542 if (FLAGS_reset_status) {
543 LOG(INFO) << "Setting Update Engine status to idle ...";
544 if (!ResetStatus()) {
545 LOG(ERROR) << "ResetStatus failed.";
546 return 1;
547 }
Gilad Arnold50c60632013-01-25 10:27:19 -0800548 LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n"
549 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} "
550 "| sed 's/^[^0-9]*//')-1)) $D;)";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700551 }
Darin Petkov58529db2010-08-13 09:19:47 -0700552
Alex Deymof4867c42013-06-28 14:41:39 -0700553 // Changes the current update over cellular network setting.
554 if (!FLAGS_update_over_cellular.empty()) {
555 gboolean allowed = FLAGS_update_over_cellular == "yes";
556 if (!allowed && FLAGS_update_over_cellular != "no") {
557 LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular
558 << "\". Please specify \"yes\" or \"no\".";
559 } else {
560 SetUpdateOverCellularPermission(allowed);
561 }
562 }
563
564 // Show the current update over cellular network setting.
565 if (FLAGS_show_update_over_cellular) {
566 bool allowed = GetUpdateOverCellularPermission();
567 LOG(INFO) << "Current update over cellular network setting: "
568 << (allowed ? "ENABLED" : "DISABLED");
569 }
570
Chris Sosacb7fa882013-07-25 17:02:59 -0700571 if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) {
Chris Sosa192449e2013-10-28 14:16:19 -0700572 LOG(ERROR) << "powerwash flag only makes sense rollback or channel change";
Chris Sosacb7fa882013-07-25 17:02:59 -0700573 return 1;
574 }
575
Alex Deymo5fdf7762013-07-17 20:01:40 -0700576 // Change the P2P enabled setting.
577 if (!FLAGS_p2p_update.empty()) {
578 gboolean enabled = FLAGS_p2p_update == "yes";
579 if (!enabled && FLAGS_p2p_update != "no") {
580 LOG(ERROR) << "Unknown option: \"" << FLAGS_p2p_update
581 << "\". Please specify \"yes\" or \"no\".";
582 } else {
583 SetP2PUpdatePermission(enabled);
584 }
585 }
586
Alex Vakulenko59e253e2014-02-24 10:40:21 -0800587 // Show the rollback availability.
588 if (FLAGS_can_rollback) {
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700589 std::string rollback_partition = GetRollbackPartition();
Chris Sosaf5c0b9c2014-04-17 16:12:03 -0700590 bool can_rollback = true;
591 if (rollback_partition.empty()) {
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700592 rollback_partition = "UNAVAILABLE";
Chris Sosaf5c0b9c2014-04-17 16:12:03 -0700593 can_rollback = false;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700594 } else {
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700595 rollback_partition = "AVAILABLE: " + rollback_partition;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700596 }
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700597
598 LOG(INFO) << "Rollback partition: " << rollback_partition;
Chris Sosaf5c0b9c2014-04-17 16:12:03 -0700599 if (!can_rollback) {
600 return 1;
601 }
Alex Vakulenko59e253e2014-02-24 10:40:21 -0800602 }
603
Alex Deymo5fdf7762013-07-17 20:01:40 -0700604 // Show the current P2P enabled setting.
605 if (FLAGS_show_p2p_update) {
606 bool enabled = GetP2PUpdatePermission();
607 LOG(INFO) << "Current update using P2P setting: "
608 << (enabled ? "ENABLED" : "DISABLED");
609 }
610
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700611 // First, update the target channel if requested.
612 if (!FLAGS_channel.empty())
Chris Sosacb7fa882013-07-25 17:02:59 -0700613 SetTargetChannel(FLAGS_channel, FLAGS_powerwash);
Darin Petkov8daa3242010-10-25 13:28:47 -0700614
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700615 // Show the current and target channels if requested.
616 if (FLAGS_show_channel) {
617 string current_channel = GetChannel(true);
618 LOG(INFO) << "Current Channel: " << current_channel;
619
620 string target_channel = GetChannel(false);
621 if (!target_channel.empty())
622 LOG(INFO) << "Target Channel (pending update): " << target_channel;
Satoru Takabayashi583667b2010-10-27 13:09:57 +0900623 }
624
Chris Sosad317e402013-06-12 13:47:09 -0700625 bool do_update_request = FLAGS_check_for_update | FLAGS_update |
626 !FLAGS_app_version.empty() | !FLAGS_omaha_url.empty();
Chris Sosa192449e2013-10-28 14:16:19 -0700627 if (FLAGS_update)
628 FLAGS_follow = true;
Chris Sosad317e402013-06-12 13:47:09 -0700629
Chris Sosad317e402013-06-12 13:47:09 -0700630 if (do_update_request && FLAGS_rollback) {
Chris Sosa192449e2013-10-28 14:16:19 -0700631 LOG(ERROR) << "Incompatible flags specified with rollback."
632 << "Rollback should not include update-related flags.";
Chris Sosad317e402013-06-12 13:47:09 -0700633 return 1;
634 }
635
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700636 if (FLAGS_rollback) {
Chris Sosad317e402013-06-12 13:47:09 -0700637 LOG(INFO) << "Requesting rollback.";
638 CHECK(Rollback(FLAGS_powerwash)) << "Request for rollback failed.";
Chris Sosad317e402013-06-12 13:47:09 -0700639 }
640
Darin Petkov58529db2010-08-13 09:19:47 -0700641 // Initiate an update check, if necessary.
Chris Sosad317e402013-06-12 13:47:09 -0700642 if (do_update_request) {
Darin Petkov296889c2010-07-23 16:20:54 -0700643 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
Darin Petkov58529db2010-08-13 09:19:47 -0700644 string app_version = FLAGS_app_version;
645 if (FLAGS_update && app_version.empty()) {
646 app_version = "ForcedUpdate";
647 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700648 }
Darin Petkov58529db2010-08-13 09:19:47 -0700649 LOG(INFO) << "Initiating update check and install.";
650 CHECK(CheckForUpdates(app_version, FLAGS_omaha_url))
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700651 << "Update check/initiate update failed.";
Chris Sosa192449e2013-10-28 14:16:19 -0700652 }
Darin Petkov58529db2010-08-13 09:19:47 -0700653
Chris Sosa192449e2013-10-28 14:16:19 -0700654 // These final options are all mutually exclusive with one another.
David Zeuthen9d73a722014-04-04 14:52:46 -0700655 if (FLAGS_follow + FLAGS_watch_for_updates + FLAGS_reboot +
656 FLAGS_status + FLAGS_is_reboot_needed +
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700657 FLAGS_block_until_reboot_is_needed > 1) {
Chris Sosa192449e2013-10-28 14:16:19 -0700658 LOG(ERROR) << "Multiple exclusive options selected. "
659 << "Select only one of --follow, --watch_for_updates, --reboot, "
David Zeuthen9d73a722014-04-04 14:52:46 -0700660 << "--is_reboot_needed, --block_until_reboot_is_needed, "
Chris Sosa192449e2013-10-28 14:16:19 -0700661 << "or --status.";
662 return 1;
663 }
664
665 if (FLAGS_status) {
666 LOG(INFO) << "Querying Update Engine status...";
667 if (!GetStatus(NULL)) {
668 LOG(ERROR) << "GetStatus failed.";
Darin Petkov58529db2010-08-13 09:19:47 -0700669 return 1;
670 }
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700671 return 0;
672 }
Darin Petkov58529db2010-08-13 09:19:47 -0700673
Chris Sosa192449e2013-10-28 14:16:19 -0700674 if (FLAGS_follow) {
675 LOG(INFO) << "Waiting for update to complete.";
676 CompleteUpdate(); // Should never return.
677 return 1;
678 }
679
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700680 if (FLAGS_watch_for_updates) {
681 LOG(INFO) << "Watching for status updates.";
682 WatchForUpdates(); // Should never return.
683 return 1;
684 }
Darin Petkov58529db2010-08-13 09:19:47 -0700685
Darin Petkov296889c2010-07-23 16:20:54 -0700686 if (FLAGS_reboot) {
687 LOG(INFO) << "Requesting a reboot...";
688 CHECK(RebootIfNeeded());
689 return 0;
690 }
Andrew de los Reyesada42202010-07-15 22:23:20 -0700691
Alex Vakulenkodea2eac2014-03-14 15:56:59 -0700692 if (FLAGS_prev_version) {
693 ShowPrevVersion();
694 }
695
Alex Vakulenko2bddadd2014-03-27 13:23:46 -0700696 if (FLAGS_show_kernels) {
697 LOG(INFO) << "Kernel partitions:\n"
698 << GetKernelDevices();
699 }
700
David Zeuthen9d73a722014-04-04 14:52:46 -0700701 if (FLAGS_is_reboot_needed) {
702 bool reboot_needed = false;
703 if (!IsRebootNeeded(&reboot_needed))
704 return 1;
705 else if (!reboot_needed)
706 return 2;
707 }
708
709 if (FLAGS_block_until_reboot_is_needed) {
710 if (!BlockUntilRebootIsNeeded())
711 return 1;
712 }
713
Darin Petkov8daa3242010-10-25 13:28:47 -0700714 LOG(INFO) << "Done.";
Andrew de los Reyes4e9b9f42010-04-26 15:06:43 -0700715 return 0;
716}