Home | History | Annotate | Download | only in update_engine
      1 //
      2 // Copyright (C) 2012 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include <inttypes.h>
     18 #include <sysexits.h>
     19 #include <unistd.h>
     20 
     21 #include <memory>
     22 #include <string>
     23 #include <vector>
     24 
     25 #include <base/bind.h>
     26 #include <base/command_line.h>
     27 #include <base/logging.h>
     28 #include <base/macros.h>
     29 #include <brillo/daemons/daemon.h>
     30 #include <brillo/flag_helper.h>
     31 
     32 #include "update_engine/common/error_code.h"
     33 #include "update_engine/common/error_code_utils.h"
     34 #include "update_engine/client.h"
     35 #include "update_engine/status_update_handler.h"
     36 #include "update_engine/update_status.h"
     37 #include "update_engine/update_status_utils.h"
     38 
     39 using chromeos_update_engine::UpdateStatusToString;
     40 using chromeos_update_engine::ErrorCode;
     41 using std::string;
     42 using std::unique_ptr;
     43 using std::vector;
     44 using update_engine::UpdateStatus;
     45 
     46 namespace {
     47 
     48 // Constant to signal that we need to continue running the daemon after
     49 // initialization.
     50 const int kContinueRunning = -1;
     51 
     52 class UpdateEngineClient : public brillo::Daemon {
     53  public:
     54   UpdateEngineClient(int argc, char** argv) : argc_(argc), argv_(argv) {
     55   }
     56 
     57   ~UpdateEngineClient() override = default;
     58 
     59  protected:
     60   int OnInit() override {
     61     int ret = Daemon::OnInit();
     62     if (ret != EX_OK) return ret;
     63 
     64     client_ = update_engine::UpdateEngineClient::CreateInstance();
     65 
     66     if (!client_) {
     67       LOG(ERROR) << "UpdateEngineService not available.";
     68       return 1;
     69     }
     70 
     71     // We can't call QuitWithExitCode from OnInit(), so we delay the execution
     72     // of the ProcessFlags method after the Daemon initialization is done.
     73     base::MessageLoop::current()->PostTask(
     74         FROM_HERE,
     75         base::Bind(&UpdateEngineClient::ProcessFlagsAndExit,
     76                    base::Unretained(this)));
     77     return EX_OK;
     78   }
     79 
     80  private:
     81   // Show the status of the update engine in stdout.
     82   bool ShowStatus();
     83 
     84   // Return whether we need to reboot. 0 if reboot is needed, 1 if an error
     85   // occurred, 2 if no reboot is needed.
     86   int GetNeedReboot();
     87 
     88   // Main method that parses and triggers all the actions based on the passed
     89   // flags. Returns the exit code of the program of kContinueRunning if it
     90   // should not exit.
     91   int ProcessFlags();
     92 
     93   // Processes the flags and exits the program accordingly.
     94   void ProcessFlagsAndExit();
     95 
     96   // Copy of argc and argv passed to main().
     97   int argc_;
     98   char** argv_;
     99 
    100   // Library-based client
    101   unique_ptr<update_engine::UpdateEngineClient> client_;
    102 
    103   // Pointers to handlers for cleanup
    104   vector<unique_ptr<update_engine::StatusUpdateHandler>> handlers_;
    105 
    106   // Tell whether the UpdateEngine service is available after startup.
    107   bool service_is_available_{false};
    108 
    109   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClient);
    110 };
    111 
    112 class ExitingStatusUpdateHandler : public update_engine::StatusUpdateHandler {
    113  public:
    114   ~ExitingStatusUpdateHandler() override = default;
    115 
    116   void IPCError(const string& error) override;
    117 };
    118 
    119 void ExitingStatusUpdateHandler::IPCError(const string& error) {
    120   LOG(ERROR) << error;
    121   exit(1);
    122 }
    123 
    124 class WatchingStatusUpdateHandler : public ExitingStatusUpdateHandler {
    125  public:
    126   ~WatchingStatusUpdateHandler() override = default;
    127 
    128   void HandleStatusUpdate(int64_t last_checked_time,
    129                           double progress,
    130                           UpdateStatus current_operation,
    131                           const string& new_version,
    132                           int64_t new_size) override;
    133 };
    134 
    135 void WatchingStatusUpdateHandler::HandleStatusUpdate(
    136     int64_t last_checked_time, double progress, UpdateStatus current_operation,
    137     const string& new_version, int64_t new_size) {
    138   LOG(INFO) << "Got status update:";
    139   LOG(INFO) << "  last_checked_time: " << last_checked_time;
    140   LOG(INFO) << "  progress: " << progress;
    141   LOG(INFO) << "  current_operation: "
    142             << UpdateStatusToString(current_operation);
    143   LOG(INFO) << "  new_version: " << new_version;
    144   LOG(INFO) << "  new_size: " << new_size;
    145 }
    146 
    147 bool UpdateEngineClient::ShowStatus() {
    148   int64_t last_checked_time = 0;
    149   double progress = 0.0;
    150   UpdateStatus current_op;
    151   string new_version;
    152   int64_t new_size = 0;
    153 
    154   if (!client_->GetStatus(&last_checked_time, &progress, &current_op,
    155                           &new_version, &new_size)) {
    156     return false;
    157   }
    158 
    159   printf("LAST_CHECKED_TIME=%" PRIi64
    160          "\nPROGRESS=%f\nCURRENT_OP=%s\n"
    161          "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n",
    162          last_checked_time, progress, UpdateStatusToString(current_op),
    163          new_version.c_str(), new_size);
    164 
    165   return true;
    166 }
    167 
    168 int UpdateEngineClient::GetNeedReboot() {
    169   int64_t last_checked_time = 0;
    170   double progress = 0.0;
    171   UpdateStatus current_op;
    172   string new_version;
    173   int64_t new_size = 0;
    174 
    175   if (!client_->GetStatus(&last_checked_time, &progress, &current_op,
    176                           &new_version, &new_size)) {
    177     return 1;
    178   }
    179 
    180   if (current_op == UpdateStatus::UPDATED_NEED_REBOOT) {
    181     return 0;
    182   }
    183 
    184   return 2;
    185 }
    186 
    187 class UpdateWaitHandler : public ExitingStatusUpdateHandler {
    188  public:
    189   explicit UpdateWaitHandler(bool exit_on_error)
    190       : exit_on_error_(exit_on_error) {}
    191 
    192   ~UpdateWaitHandler() override = default;
    193 
    194   void HandleStatusUpdate(int64_t last_checked_time,
    195                           double progress,
    196                           UpdateStatus current_operation,
    197                           const string& new_version,
    198                           int64_t new_size) override;
    199 
    200  private:
    201   bool exit_on_error_;
    202 };
    203 
    204 void UpdateWaitHandler::HandleStatusUpdate(int64_t /* last_checked_time */,
    205                                            double /* progress */,
    206                                            UpdateStatus current_operation,
    207                                            const string& /* new_version */,
    208                                            int64_t /* new_size */) {
    209   if (exit_on_error_ && current_operation == UpdateStatus::IDLE) {
    210     LOG(ERROR) << "Update failed, current operations is "
    211                << UpdateStatusToString(current_operation);
    212     exit(1);
    213   }
    214   if (current_operation == UpdateStatus::UPDATED_NEED_REBOOT) {
    215     LOG(INFO) << "Update succeeded -- reboot needed.";
    216     exit(0);
    217   }
    218 }
    219 
    220 int UpdateEngineClient::ProcessFlags() {
    221   DEFINE_string(app_version, "", "Force the current app version.");
    222   DEFINE_string(channel, "",
    223                 "Set the target channel. The device will be powerwashed if the "
    224                 "target channel is more stable than the current channel unless "
    225                 "--nopowerwash is specified.");
    226   DEFINE_bool(check_for_update, false, "Initiate check for updates.");
    227   DEFINE_bool(follow, false,
    228               "Wait for any update operations to complete."
    229               "Exit status is 0 if the update succeeded, and 1 otherwise.");
    230   DEFINE_bool(interactive, true, "Mark the update request as interactive.");
    231   DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
    232   DEFINE_string(p2p_update, "",
    233                 "Enables (\"yes\") or disables (\"no\") the peer-to-peer update"
    234                 " sharing.");
    235   DEFINE_bool(powerwash, true,
    236               "When performing rollback or channel change, "
    237               "do a powerwash or allow it respectively.");
    238   DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
    239   DEFINE_bool(is_reboot_needed, false,
    240               "Exit status 0 if reboot is needed, "
    241               "2 if reboot is not needed or 1 if an error occurred.");
    242   DEFINE_bool(block_until_reboot_is_needed, false,
    243               "Blocks until reboot is "
    244               "needed. Returns non-zero exit status if an error occurred.");
    245   DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
    246   DEFINE_bool(rollback, false,
    247               "Perform a rollback to the previous partition. The device will "
    248               "be powerwashed unless --nopowerwash is specified.");
    249   DEFINE_bool(can_rollback, false,
    250               "Shows whether rollback partition "
    251               "is available.");
    252   DEFINE_bool(show_channel, false, "Show the current and target channels.");
    253   DEFINE_bool(show_p2p_update, false,
    254               "Show the current setting for peer-to-peer update sharing.");
    255   DEFINE_bool(show_update_over_cellular, false,
    256               "Show the current setting for updates over cellular networks.");
    257   DEFINE_bool(status, false, "Print the status to stdout.");
    258   DEFINE_bool(update, false,
    259               "Forces an update and waits for it to complete. "
    260               "Implies --follow.");
    261   DEFINE_string(update_over_cellular, "",
    262                 "Enables (\"yes\") or disables (\"no\") the updates over "
    263                 "cellular networks.");
    264   DEFINE_bool(watch_for_updates, false,
    265               "Listen for status updates and print them to the screen.");
    266   DEFINE_bool(prev_version, false,
    267               "Show the previous OS version used before the update reboot.");
    268   DEFINE_bool(last_attempt_error, false, "Show the last attempt error.");
    269 
    270   // Boilerplate init commands.
    271   base::CommandLine::Init(argc_, argv_);
    272   brillo::FlagHelper::Init(argc_, argv_, "Chromium OS Update Engine Client");
    273 
    274   // Ensure there are no positional arguments.
    275   const vector<string> positional_args =
    276       base::CommandLine::ForCurrentProcess()->GetArgs();
    277   if (!positional_args.empty()) {
    278     LOG(ERROR) << "Found a positional argument '" << positional_args.front()
    279                << "'. If you want to pass a value to a flag, pass it as "
    280                   "--flag=value.";
    281     return 1;
    282   }
    283 
    284   // Update the status if requested.
    285   if (FLAGS_reset_status) {
    286     LOG(INFO) << "Setting Update Engine status to idle ...";
    287 
    288     if (client_->ResetStatus()) {
    289       LOG(INFO) << "ResetStatus succeeded; to undo partition table changes "
    290                    "run:\n"
    291                    "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo "
    292                    "${P#$D} | sed 's/^[^0-9]*//')-1)) $D;)";
    293     } else {
    294       LOG(ERROR) << "ResetStatus failed";
    295       return 1;
    296     }
    297   }
    298 
    299   // Changes the current update over cellular network setting.
    300   if (!FLAGS_update_over_cellular.empty()) {
    301     bool allowed = FLAGS_update_over_cellular == "yes";
    302     if (!allowed && FLAGS_update_over_cellular != "no") {
    303       LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular
    304                  << "\". Please specify \"yes\" or \"no\".";
    305     } else {
    306       if (!client_->SetUpdateOverCellularPermission(allowed)) {
    307         LOG(ERROR) << "Error setting the update over cellular setting.";
    308         return 1;
    309       }
    310     }
    311   }
    312 
    313   // Show the current update over cellular network setting.
    314   if (FLAGS_show_update_over_cellular) {
    315     bool allowed;
    316 
    317     if (!client_->GetUpdateOverCellularPermission(&allowed)) {
    318       LOG(ERROR) << "Error getting the update over cellular setting.";
    319       return 1;
    320     }
    321 
    322     LOG(INFO) << "Current update over cellular network setting: "
    323               << (allowed ? "ENABLED" : "DISABLED");
    324   }
    325 
    326   if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) {
    327     LOG(ERROR) << "powerwash flag only makes sense rollback or channel change";
    328     return 1;
    329   }
    330 
    331   // Change the P2P enabled setting.
    332   if (!FLAGS_p2p_update.empty()) {
    333     bool enabled = FLAGS_p2p_update == "yes";
    334     if (!enabled && FLAGS_p2p_update != "no") {
    335       LOG(ERROR) << "Unknown option: \"" << FLAGS_p2p_update
    336                  << "\". Please specify \"yes\" or \"no\".";
    337     } else {
    338       if (!client_->SetP2PUpdatePermission(enabled)) {
    339         LOG(ERROR) << "Error setting the peer-to-peer update setting.";
    340         return 1;
    341       }
    342     }
    343   }
    344 
    345   // Show the rollback availability.
    346   if (FLAGS_can_rollback) {
    347     string rollback_partition;
    348 
    349     if (!client_->GetRollbackPartition(&rollback_partition)) {
    350       LOG(ERROR) << "Error while querying rollback partition availabilty.";
    351       return 1;
    352     }
    353 
    354     bool can_rollback = true;
    355     if (rollback_partition.empty()) {
    356       rollback_partition = "UNAVAILABLE";
    357       can_rollback = false;
    358     } else {
    359       rollback_partition = "AVAILABLE: " + rollback_partition;
    360     }
    361 
    362     LOG(INFO) << "Rollback partition: " << rollback_partition;
    363     if (!can_rollback) {
    364       return 1;
    365     }
    366   }
    367 
    368   // Show the current P2P enabled setting.
    369   if (FLAGS_show_p2p_update) {
    370     bool enabled;
    371 
    372     if (!client_->GetP2PUpdatePermission(&enabled)) {
    373       LOG(ERROR) << "Error getting the peer-to-peer update setting.";
    374       return 1;
    375     }
    376 
    377     LOG(INFO) << "Current update using P2P setting: "
    378               << (enabled ? "ENABLED" : "DISABLED");
    379   }
    380 
    381   // First, update the target channel if requested.
    382   if (!FLAGS_channel.empty()) {
    383     if (!client_->SetTargetChannel(FLAGS_channel, FLAGS_powerwash)) {
    384       LOG(ERROR) << "Error setting the channel.";
    385       return 1;
    386     }
    387 
    388     LOG(INFO) << "Channel permanently set to: " << FLAGS_channel;
    389   }
    390 
    391   // Show the current and target channels if requested.
    392   if (FLAGS_show_channel) {
    393     string current_channel;
    394     string target_channel;
    395 
    396     if (!client_->GetChannel(&current_channel)) {
    397       LOG(ERROR) << "Error getting the current channel.";
    398       return 1;
    399     }
    400 
    401     if (!client_->GetTargetChannel(&target_channel)) {
    402       LOG(ERROR) << "Error getting the target channel.";
    403       return 1;
    404     }
    405 
    406     LOG(INFO) << "Current Channel: " << current_channel;
    407 
    408     if (!target_channel.empty())
    409       LOG(INFO) << "Target Channel (pending update): " << target_channel;
    410   }
    411 
    412   bool do_update_request = FLAGS_check_for_update | FLAGS_update |
    413                            !FLAGS_app_version.empty() |
    414                            !FLAGS_omaha_url.empty();
    415   if (FLAGS_update) FLAGS_follow = true;
    416 
    417   if (do_update_request && FLAGS_rollback) {
    418     LOG(ERROR) << "Incompatible flags specified with rollback."
    419                << "Rollback should not include update-related flags.";
    420     return 1;
    421   }
    422 
    423   if (FLAGS_rollback) {
    424     LOG(INFO) << "Requesting rollback.";
    425     if (!client_->Rollback(FLAGS_powerwash)) {
    426       LOG(ERROR) << "Rollback request failed.";
    427       return 1;
    428     }
    429   }
    430 
    431   // Initiate an update check, if necessary.
    432   if (do_update_request) {
    433     LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
    434     string app_version = FLAGS_app_version;
    435     if (FLAGS_update && app_version.empty()) {
    436       app_version = "ForcedUpdate";
    437       LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
    438     }
    439     LOG(INFO) << "Initiating update check and install.";
    440     if (!client_->AttemptUpdate(app_version, FLAGS_omaha_url,
    441                                 FLAGS_interactive)) {
    442       LOG(ERROR) << "Error checking for update.";
    443       return 1;
    444     }
    445   }
    446 
    447   // These final options are all mutually exclusive with one another.
    448   if (FLAGS_follow + FLAGS_watch_for_updates + FLAGS_reboot + FLAGS_status +
    449           FLAGS_is_reboot_needed + FLAGS_block_until_reboot_is_needed >
    450       1) {
    451     LOG(ERROR) << "Multiple exclusive options selected. "
    452                << "Select only one of --follow, --watch_for_updates, --reboot, "
    453                << "--is_reboot_needed, --block_until_reboot_is_needed, "
    454                << "or --status.";
    455     return 1;
    456   }
    457 
    458   if (FLAGS_status) {
    459     LOG(INFO) << "Querying Update Engine status...";
    460     if (!ShowStatus()) {
    461       LOG(ERROR) << "Failed to query status";
    462       return 1;
    463     }
    464     return 0;
    465   }
    466 
    467   if (FLAGS_follow) {
    468     LOG(INFO) << "Waiting for update to complete.";
    469     auto handler = new UpdateWaitHandler(true);
    470     handlers_.emplace_back(handler);
    471     client_->RegisterStatusUpdateHandler(handler);
    472     return kContinueRunning;
    473   }
    474 
    475   if (FLAGS_watch_for_updates) {
    476     LOG(INFO) << "Watching for status updates.";
    477     auto handler = new WatchingStatusUpdateHandler();
    478     handlers_.emplace_back(handler);
    479     client_->RegisterStatusUpdateHandler(handler);
    480     return kContinueRunning;
    481   }
    482 
    483   if (FLAGS_reboot) {
    484     LOG(INFO) << "Requesting a reboot...";
    485     client_->RebootIfNeeded();
    486     return 0;
    487   }
    488 
    489   if (FLAGS_prev_version) {
    490     string prev_version;
    491 
    492     if (!client_->GetPrevVersion(&prev_version)) {
    493       LOG(ERROR) << "Error getting previous version.";
    494     } else {
    495       LOG(INFO) << "Previous version = " << prev_version;
    496     }
    497   }
    498 
    499   if (FLAGS_is_reboot_needed) {
    500     int ret = GetNeedReboot();
    501 
    502     if (ret == 1) {
    503       LOG(ERROR) << "Could not query the current operation.";
    504     }
    505 
    506     return ret;
    507   }
    508 
    509   if (FLAGS_block_until_reboot_is_needed) {
    510     auto handler = new UpdateWaitHandler(false);
    511     handlers_.emplace_back(handler);
    512     client_->RegisterStatusUpdateHandler(handler);
    513     return kContinueRunning;
    514   }
    515 
    516   if (FLAGS_last_attempt_error) {
    517     int last_attempt_error;
    518     if (!client_->GetLastAttemptError(&last_attempt_error)) {
    519       LOG(ERROR) << "Error getting last attempt error.";
    520     } else {
    521       ErrorCode code = static_cast<ErrorCode>(last_attempt_error);
    522       string error_msg = chromeos_update_engine::utils::ErrorCodeToString(code);
    523       printf("ERROR_CODE=%i\n"
    524              "ERROR_MESSAGE=%s\n",
    525              last_attempt_error, error_msg.c_str());
    526     }
    527  }
    528 
    529   return 0;
    530 }
    531 
    532 void UpdateEngineClient::ProcessFlagsAndExit() {
    533   int ret = ProcessFlags();
    534   if (ret != kContinueRunning)
    535     QuitWithExitCode(ret);
    536 }
    537 
    538 }  // namespace
    539 
    540 int main(int argc, char** argv) {
    541   UpdateEngineClient client(argc, argv);
    542   return client.Run();
    543 }
    544