| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| |
| #include <base/command_line.h> |
| #include <base/logging.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <chromeos/flag_helper.h> |
| #include <dbus/dbus.h> |
| #include <glib.h> |
| #include <inttypes.h> |
| |
| #include "update_engine/dbus_constants.h" |
| #include "update_engine/glib_utils.h" |
| |
| extern "C" { |
| #include "update_engine/org.chromium.UpdateEngineInterface.dbusclient.h" |
| } |
| |
| using chromeos_update_engine::AttemptUpdateFlags; |
| using chromeos_update_engine::kAttemptUpdateFlagNonInteractive; |
| using chromeos_update_engine::kUpdateEngineServiceInterface; |
| using chromeos_update_engine::kUpdateEngineServiceName; |
| using chromeos_update_engine::kUpdateEngineServicePath; |
| using chromeos_update_engine::utils::GetAndFreeGError; |
| using std::string; |
| |
| namespace { |
| |
| bool GetProxy(DBusGProxy** out_proxy) { |
| DBusGConnection* bus; |
| DBusGProxy* proxy = nullptr; |
| GError* error = nullptr; |
| const int kTries = 4; |
| const int kRetrySeconds = 10; |
| |
| bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); |
| if (bus == nullptr) { |
| LOG(ERROR) << "Failed to get bus: " << GetAndFreeGError(&error); |
| exit(1); |
| } |
| for (int i = 0; !proxy && i < kTries; ++i) { |
| if (i > 0) { |
| LOG(INFO) << "Retrying to get dbus proxy. Try " |
| << (i + 1) << "/" << kTries; |
| g_usleep(kRetrySeconds * G_USEC_PER_SEC); |
| } |
| proxy = dbus_g_proxy_new_for_name_owner(bus, |
| kUpdateEngineServiceName, |
| kUpdateEngineServicePath, |
| kUpdateEngineServiceInterface, |
| &error); |
| LOG_IF(WARNING, !proxy) << "Error getting dbus proxy for " |
| << kUpdateEngineServiceName << ": " |
| << GetAndFreeGError(&error); |
| } |
| if (proxy == nullptr) { |
| LOG(ERROR) << "Giving up -- unable to get dbus proxy for " |
| << kUpdateEngineServiceName; |
| exit(1); |
| } |
| *out_proxy = proxy; |
| return true; |
| } |
| |
| static void StatusUpdateSignalHandler(DBusGProxy* proxy, |
| int64_t last_checked_time, |
| double progress, |
| gchar* current_operation, |
| gchar* new_version, |
| int64_t new_size, |
| void* user_data) { |
| LOG(INFO) << "Got status update:"; |
| LOG(INFO) << " last_checked_time: " << last_checked_time; |
| LOG(INFO) << " progress: " << progress; |
| LOG(INFO) << " current_operation: " << current_operation; |
| LOG(INFO) << " new_version: " << new_version; |
| LOG(INFO) << " new_size: " << new_size; |
| } |
| |
| bool ResetStatus() { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean rc = update_engine_client_reset_status(proxy, &error); |
| return rc; |
| } |
| |
| |
| // If |op| is non-null, sets it to the current operation string or an |
| // empty string if unable to obtain the current status. |
| bool GetStatus(string* op) { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gint64 last_checked_time = 0; |
| gdouble progress = 0.0; |
| char* current_op = nullptr; |
| char* new_version = nullptr; |
| gint64 new_size = 0; |
| |
| gboolean rc = update_engine_client_get_status(proxy, |
| &last_checked_time, |
| &progress, |
| ¤t_op, |
| &new_version, |
| &new_size, |
| &error); |
| if (rc == FALSE) { |
| LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error); |
| } |
| printf("LAST_CHECKED_TIME=%" PRIi64 "\nPROGRESS=%f\nCURRENT_OP=%s\n" |
| "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n", |
| last_checked_time, |
| progress, |
| current_op, |
| new_version, |
| new_size); |
| if (op) { |
| *op = current_op ? current_op : ""; |
| } |
| return true; |
| } |
| |
| // Should never return. |
| void WatchForUpdates() { |
| DBusGProxy* proxy; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| // Register marshaller |
| dbus_g_object_register_marshaller( |
| g_cclosure_marshal_generic, |
| G_TYPE_NONE, |
| G_TYPE_INT64, |
| G_TYPE_DOUBLE, |
| G_TYPE_STRING, |
| G_TYPE_STRING, |
| G_TYPE_INT64, |
| G_TYPE_INVALID); |
| |
| static const char kStatusUpdate[] = "StatusUpdate"; |
| dbus_g_proxy_add_signal(proxy, |
| kStatusUpdate, |
| G_TYPE_INT64, |
| G_TYPE_DOUBLE, |
| G_TYPE_STRING, |
| G_TYPE_STRING, |
| G_TYPE_INT64, |
| G_TYPE_INVALID); |
| GMainLoop* loop = g_main_loop_new(nullptr, TRUE); |
| dbus_g_proxy_connect_signal(proxy, |
| kStatusUpdate, |
| G_CALLBACK(StatusUpdateSignalHandler), |
| nullptr, |
| nullptr); |
| g_main_loop_run(loop); |
| g_main_loop_unref(loop); |
| } |
| |
| bool Rollback(bool rollback) { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean rc = update_engine_client_attempt_rollback(proxy, |
| rollback, |
| &error); |
| CHECK_EQ(rc, TRUE) << "Error with rollback request: " |
| << GetAndFreeGError(&error); |
| return true; |
| } |
| |
| string GetRollbackPartition() { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| char* rollback_partition = nullptr; |
| gboolean rc = update_engine_client_get_rollback_partition(proxy, |
| &rollback_partition, |
| &error); |
| CHECK_EQ(rc, TRUE) << "Error while querying rollback partition availabilty: " |
| << GetAndFreeGError(&error); |
| string partition = rollback_partition; |
| g_free(rollback_partition); |
| return partition; |
| } |
| |
| string GetKernelDevices() { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| char* kernel_devices = nullptr; |
| gboolean rc = update_engine_client_get_kernel_devices(proxy, |
| &kernel_devices, |
| &error); |
| CHECK_EQ(rc, TRUE) << "Error while getting a list of kernel devices: " |
| << GetAndFreeGError(&error); |
| string devices = kernel_devices; |
| g_free(kernel_devices); |
| return devices; |
| } |
| |
| bool CheckForUpdates(const string& app_version, |
| const string& omaha_url, |
| bool interactive) { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>( |
| interactive ? 0 : kAttemptUpdateFlagNonInteractive); |
| gboolean rc = |
| update_engine_client_attempt_update_with_flags(proxy, |
| app_version.c_str(), |
| omaha_url.c_str(), |
| static_cast<gint>(flags), |
| &error); |
| CHECK_EQ(rc, TRUE) << "Error checking for update: " |
| << GetAndFreeGError(&error); |
| return true; |
| } |
| |
| bool RebootIfNeeded() { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean rc = |
| update_engine_client_reboot_if_needed(proxy, &error); |
| // Reboot error code doesn't necessarily mean that a reboot |
| // failed. For example, D-Bus may be shutdown before we receive the |
| // result. |
| LOG_IF(INFO, !rc) << "Reboot error message: " << GetAndFreeGError(&error); |
| return true; |
| } |
| |
| void SetTargetChannel(const string& target_channel, bool allow_powerwash) { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean rc = update_engine_client_set_channel(proxy, |
| target_channel.c_str(), |
| allow_powerwash, |
| &error); |
| CHECK_EQ(rc, true) << "Error setting the channel: " |
| << GetAndFreeGError(&error); |
| LOG(INFO) << "Channel permanently set to: " << target_channel; |
| } |
| |
| string GetChannel(bool get_current_channel) { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| char* channel = nullptr; |
| gboolean rc = update_engine_client_get_channel(proxy, |
| get_current_channel, |
| &channel, |
| &error); |
| CHECK_EQ(rc, true) << "Error getting the channel: " |
| << GetAndFreeGError(&error); |
| string output = channel; |
| g_free(channel); |
| return output; |
| } |
| |
| void SetUpdateOverCellularPermission(gboolean allowed) { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean rc = update_engine_client_set_update_over_cellular_permission( |
| proxy, |
| allowed, |
| &error); |
| CHECK_EQ(rc, true) << "Error setting the update over cellular setting: " |
| << GetAndFreeGError(&error); |
| } |
| |
| bool GetUpdateOverCellularPermission() { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean allowed; |
| gboolean rc = update_engine_client_get_update_over_cellular_permission( |
| proxy, |
| &allowed, |
| &error); |
| CHECK_EQ(rc, true) << "Error getting the update over cellular setting: " |
| << GetAndFreeGError(&error); |
| return allowed; |
| } |
| |
| void SetP2PUpdatePermission(gboolean enabled) { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean rc = update_engine_client_set_p2p_update_permission( |
| proxy, |
| enabled, |
| &error); |
| CHECK_EQ(rc, true) << "Error setting the peer-to-peer update setting: " |
| << GetAndFreeGError(&error); |
| } |
| |
| bool GetP2PUpdatePermission() { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| gboolean enabled; |
| gboolean rc = update_engine_client_get_p2p_update_permission( |
| proxy, |
| &enabled, |
| &error); |
| CHECK_EQ(rc, true) << "Error getting the peer-to-peer update setting: " |
| << GetAndFreeGError(&error); |
| return enabled; |
| } |
| |
| static gboolean CompleteUpdateSource(gpointer data) { |
| string current_op; |
| if (!GetStatus(¤t_op) || current_op == "UPDATE_STATUS_IDLE") { |
| LOG(ERROR) << "Update failed."; |
| exit(1); |
| } |
| if (current_op == "UPDATE_STATUS_UPDATED_NEED_REBOOT") { |
| LOG(INFO) << "Update succeeded -- reboot needed."; |
| exit(0); |
| } |
| return TRUE; |
| } |
| |
| // This is similar to watching for updates but rather than registering |
| // a signal watch, actively poll the daemon just in case it stops |
| // sending notifications. |
| void CompleteUpdate() { |
| GMainLoop* loop = g_main_loop_new(nullptr, TRUE); |
| g_timeout_add_seconds(5, CompleteUpdateSource, nullptr); |
| g_main_loop_run(loop); |
| g_main_loop_unref(loop); |
| } |
| |
| void ShowPrevVersion() { |
| DBusGProxy* proxy; |
| GError* error = nullptr; |
| |
| CHECK(GetProxy(&proxy)); |
| |
| char* prev_version = nullptr; |
| |
| gboolean rc = update_engine_client_get_prev_version(proxy, |
| &prev_version, |
| &error); |
| if (!rc) { |
| LOG(ERROR) << "Error getting previous version: " |
| << GetAndFreeGError(&error); |
| } else { |
| LOG(INFO) << "Previous version = " << prev_version; |
| g_free(prev_version); |
| } |
| } |
| |
| bool CheckIfRebootIsNeeded(DBusGProxy *proxy, bool *out_reboot_needed) { |
| gint64 last_checked_time = 0; |
| gdouble progress = 0.0; |
| char* current_op = nullptr; |
| char* new_version = nullptr; |
| gint64 new_size = 0; |
| GError* error = nullptr; |
| |
| if (!update_engine_client_get_status(proxy, |
| &last_checked_time, |
| &progress, |
| ¤t_op, |
| &new_version, |
| &new_size, |
| &error)) { |
| LOG(INFO) << "Error getting status: " << GetAndFreeGError(&error); |
| return false; |
| } |
| *out_reboot_needed = |
| (g_strcmp0(current_op, |
| update_engine::kUpdateStatusUpdatedNeedReboot) == 0); |
| g_free(current_op); |
| g_free(new_version); |
| return true; |
| } |
| |
| // Determines if reboot is needed. The result is returned in |
| // |out_reboot_needed|. Returns true if the check succeeded, false |
| // otherwise. |
| bool IsRebootNeeded(bool *out_reboot_needed) { |
| DBusGProxy* proxy = nullptr; |
| CHECK(GetProxy(&proxy)); |
| bool ret = CheckIfRebootIsNeeded(proxy, out_reboot_needed); |
| g_object_unref(proxy); |
| return ret; |
| } |
| |
| static void OnBlockUntilRebootStatusCallback( |
| DBusGProxy* proxy, |
| int64_t last_checked_time, |
| double progress, |
| const gchar* current_operation, |
| const gchar* new_version, |
| int64_t new_size, |
| void* user_data) { |
| GMainLoop *loop = reinterpret_cast<GMainLoop*>(user_data); |
| if (g_strcmp0(current_operation, |
| update_engine::kUpdateStatusUpdatedNeedReboot) == 0) { |
| g_main_loop_quit(loop); |
| } |
| } |
| |
| bool CheckRebootNeeded(DBusGProxy *proxy, GMainLoop *loop) { |
| bool reboot_needed; |
| if (!CheckIfRebootIsNeeded(proxy, &reboot_needed)) |
| return false; |
| if (reboot_needed) |
| return true; |
| // This will block until OnBlockUntilRebootStatusCallback() calls |
| // g_main_loop_quit(). |
| g_main_loop_run(loop); |
| return true; |
| } |
| |
| // Blocks until a reboot is needed. Returns true if waiting succeeded, |
| // false if an error occurred. |
| bool BlockUntilRebootIsNeeded() { |
| // The basic idea is to get a proxy, listen to signals and only then |
| // check the status. If no reboot is needed, just sit and wait for |
| // the StatusUpdate signal to convey that a reboot is pending. |
| DBusGProxy* proxy = nullptr; |
| CHECK(GetProxy(&proxy)); |
| dbus_g_object_register_marshaller( |
| g_cclosure_marshal_generic, |
| G_TYPE_NONE, |
| G_TYPE_INT64, |
| G_TYPE_DOUBLE, |
| G_TYPE_STRING, |
| G_TYPE_STRING, |
| G_TYPE_INT64, |
| G_TYPE_INVALID); |
| dbus_g_proxy_add_signal(proxy, |
| update_engine::kStatusUpdate, // Signal name. |
| G_TYPE_INT64, |
| G_TYPE_DOUBLE, |
| G_TYPE_STRING, |
| G_TYPE_STRING, |
| G_TYPE_INT64, |
| G_TYPE_INVALID); |
| GMainLoop* loop = g_main_loop_new(nullptr, TRUE); |
| dbus_g_proxy_connect_signal(proxy, |
| update_engine::kStatusUpdate, |
| G_CALLBACK(OnBlockUntilRebootStatusCallback), |
| loop, |
| nullptr); // free_data_func. |
| |
| bool ret = CheckRebootNeeded(proxy, loop); |
| |
| dbus_g_proxy_disconnect_signal(proxy, |
| update_engine::kStatusUpdate, |
| G_CALLBACK(OnBlockUntilRebootStatusCallback), |
| loop); |
| g_main_loop_unref(loop); |
| g_object_unref(proxy); |
| return ret; |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| DEFINE_string(app_version, "", "Force the current app version."); |
| DEFINE_string(channel, "", |
| "Set the target channel. The device will be powerwashed if the " |
| "target channel is more stable than the current channel unless " |
| "--nopowerwash is specified."); |
| DEFINE_bool(check_for_update, false, "Initiate check for updates."); |
| DEFINE_bool(follow, false, "Wait for any update operations to complete." |
| "Exit status is 0 if the update succeeded, and 1 otherwise."); |
| DEFINE_bool(interactive, true, "Mark the update request as interactive."); |
| DEFINE_string(omaha_url, "", "The URL of the Omaha update server."); |
| DEFINE_string(p2p_update, "", |
| "Enables (\"yes\") or disables (\"no\") the peer-to-peer update" |
| " sharing."); |
| DEFINE_bool(powerwash, true, "When performing rollback or channel change, " |
| "do a powerwash or allow it respectively."); |
| DEFINE_bool(reboot, false, "Initiate a reboot if needed."); |
| DEFINE_bool(is_reboot_needed, false, "Exit status 0 if reboot is needed, " |
| "2 if reboot is not needed or 1 if an error occurred."); |
| DEFINE_bool(block_until_reboot_is_needed, false, "Blocks until reboot is " |
| "needed. Returns non-zero exit status if an error occurred."); |
| DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle."); |
| DEFINE_bool(rollback, false, |
| "Perform a rollback to the previous partition. The device will " |
| "be powerwashed unless --nopowerwash is specified."); |
| DEFINE_bool(can_rollback, false, "Shows whether rollback partition " |
| "is available."); |
| DEFINE_bool(show_channel, false, "Show the current and target channels."); |
| DEFINE_bool(show_p2p_update, false, |
| "Show the current setting for peer-to-peer update sharing."); |
| DEFINE_bool(show_update_over_cellular, false, |
| "Show the current setting for updates over cellular networks."); |
| DEFINE_bool(status, false, "Print the status to stdout."); |
| DEFINE_bool(update, false, "Forces an update and waits for it to complete. " |
| "Implies --follow."); |
| DEFINE_string(update_over_cellular, "", |
| "Enables (\"yes\") or disables (\"no\") the updates over " |
| "cellular networks."); |
| DEFINE_bool(watch_for_updates, false, |
| "Listen for status updates and print them to the screen."); |
| DEFINE_bool(prev_version, false, |
| "Show the previous OS version used before the update reboot."); |
| DEFINE_bool(show_kernels, false, "Show the list of kernel patritions and " |
| "whether each of them is bootable or not"); |
| |
| // Boilerplate init commands. |
| g_type_init(); |
| dbus_threads_init_default(); |
| chromeos::FlagHelper::Init(argc, argv, "Chromium OS Update Engine Client"); |
| |
| // Ensure there are no positional arguments. |
| const std::vector<string> positional_args = |
| base::CommandLine::ForCurrentProcess()->GetArgs(); |
| if (!positional_args.empty()) { |
| LOG(ERROR) << "Found a positional argument '" << positional_args.front() |
| << "'. If you want to pass a value to a flag, pass it as " |
| "--flag=value."; |
| return 1; |
| } |
| |
| // Update the status if requested. |
| if (FLAGS_reset_status) { |
| LOG(INFO) << "Setting Update Engine status to idle ..."; |
| if (!ResetStatus()) { |
| LOG(ERROR) << "ResetStatus failed."; |
| return 1; |
| } |
| LOG(INFO) << "ResetStatus succeeded; to undo partition table changes run:\n" |
| "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo ${P#$D} " |
| "| sed 's/^[^0-9]*//')-1)) $D;)"; |
| } |
| |
| // Changes the current update over cellular network setting. |
| if (!FLAGS_update_over_cellular.empty()) { |
| gboolean allowed = FLAGS_update_over_cellular == "yes"; |
| if (!allowed && FLAGS_update_over_cellular != "no") { |
| LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular |
| << "\". Please specify \"yes\" or \"no\"."; |
| } else { |
| SetUpdateOverCellularPermission(allowed); |
| } |
| } |
| |
| // Show the current update over cellular network setting. |
| if (FLAGS_show_update_over_cellular) { |
| bool allowed = GetUpdateOverCellularPermission(); |
| LOG(INFO) << "Current update over cellular network setting: " |
| << (allowed ? "ENABLED" : "DISABLED"); |
| } |
| |
| if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) { |
| LOG(ERROR) << "powerwash flag only makes sense rollback or channel change"; |
| return 1; |
| } |
| |
| // Change the P2P enabled setting. |
| if (!FLAGS_p2p_update.empty()) { |
| gboolean enabled = FLAGS_p2p_update == "yes"; |
| if (!enabled && FLAGS_p2p_update != "no") { |
| LOG(ERROR) << "Unknown option: \"" << FLAGS_p2p_update |
| << "\". Please specify \"yes\" or \"no\"."; |
| } else { |
| SetP2PUpdatePermission(enabled); |
| } |
| } |
| |
| // Show the rollback availability. |
| if (FLAGS_can_rollback) { |
| string rollback_partition = GetRollbackPartition(); |
| bool can_rollback = true; |
| if (rollback_partition.empty()) { |
| rollback_partition = "UNAVAILABLE"; |
| can_rollback = false; |
| } else { |
| rollback_partition = "AVAILABLE: " + rollback_partition; |
| } |
| |
| LOG(INFO) << "Rollback partition: " << rollback_partition; |
| if (!can_rollback) { |
| return 1; |
| } |
| } |
| |
| // Show the current P2P enabled setting. |
| if (FLAGS_show_p2p_update) { |
| bool enabled = GetP2PUpdatePermission(); |
| LOG(INFO) << "Current update using P2P setting: " |
| << (enabled ? "ENABLED" : "DISABLED"); |
| } |
| |
| // First, update the target channel if requested. |
| if (!FLAGS_channel.empty()) |
| SetTargetChannel(FLAGS_channel, FLAGS_powerwash); |
| |
| // Show the current and target channels if requested. |
| if (FLAGS_show_channel) { |
| string current_channel = GetChannel(true); |
| LOG(INFO) << "Current Channel: " << current_channel; |
| |
| string target_channel = GetChannel(false); |
| if (!target_channel.empty()) |
| LOG(INFO) << "Target Channel (pending update): " << target_channel; |
| } |
| |
| bool do_update_request = FLAGS_check_for_update | FLAGS_update | |
| !FLAGS_app_version.empty() | !FLAGS_omaha_url.empty(); |
| if (FLAGS_update) |
| FLAGS_follow = true; |
| |
| if (do_update_request && FLAGS_rollback) { |
| LOG(ERROR) << "Incompatible flags specified with rollback." |
| << "Rollback should not include update-related flags."; |
| return 1; |
| } |
| |
| if (FLAGS_rollback) { |
| LOG(INFO) << "Requesting rollback."; |
| CHECK(Rollback(FLAGS_powerwash)) << "Request for rollback failed."; |
| } |
| |
| // Initiate an update check, if necessary. |
| if (do_update_request) { |
| LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored."; |
| string app_version = FLAGS_app_version; |
| if (FLAGS_update && app_version.empty()) { |
| app_version = "ForcedUpdate"; |
| LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate."; |
| } |
| LOG(INFO) << "Initiating update check and install."; |
| CHECK(CheckForUpdates(app_version, FLAGS_omaha_url, FLAGS_interactive)) |
| << "Update check/initiate update failed."; |
| } |
| |
| // These final options are all mutually exclusive with one another. |
| if (FLAGS_follow + FLAGS_watch_for_updates + FLAGS_reboot + |
| FLAGS_status + FLAGS_is_reboot_needed + |
| FLAGS_block_until_reboot_is_needed > 1) { |
| LOG(ERROR) << "Multiple exclusive options selected. " |
| << "Select only one of --follow, --watch_for_updates, --reboot, " |
| << "--is_reboot_needed, --block_until_reboot_is_needed, " |
| << "or --status."; |
| return 1; |
| } |
| |
| if (FLAGS_status) { |
| LOG(INFO) << "Querying Update Engine status..."; |
| if (!GetStatus(nullptr)) { |
| LOG(ERROR) << "GetStatus failed."; |
| return 1; |
| } |
| return 0; |
| } |
| |
| if (FLAGS_follow) { |
| LOG(INFO) << "Waiting for update to complete."; |
| CompleteUpdate(); // Should never return. |
| return 1; |
| } |
| |
| if (FLAGS_watch_for_updates) { |
| LOG(INFO) << "Watching for status updates."; |
| WatchForUpdates(); // Should never return. |
| return 1; |
| } |
| |
| if (FLAGS_reboot) { |
| LOG(INFO) << "Requesting a reboot..."; |
| CHECK(RebootIfNeeded()); |
| return 0; |
| } |
| |
| if (FLAGS_prev_version) { |
| ShowPrevVersion(); |
| } |
| |
| if (FLAGS_show_kernels) { |
| LOG(INFO) << "Kernel partitions:\n" |
| << GetKernelDevices(); |
| } |
| |
| if (FLAGS_is_reboot_needed) { |
| bool reboot_needed = false; |
| if (!IsRebootNeeded(&reboot_needed)) |
| return 1; |
| else if (!reboot_needed) |
| return 2; |
| } |
| |
| if (FLAGS_block_until_reboot_is_needed) { |
| if (!BlockUntilRebootIsNeeded()) |
| return 1; |
| } |
| |
| LOG(INFO) << "Done."; |
| return 0; |
| } |