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 <base/strings/string_number_conversions.h>
30 #include <base/strings/string_split.h>
31 #include <base/threading/platform_thread.h>
32 #include <base/threading/thread_task_runner_handle.h>
33 #include <brillo/daemons/daemon.h>
34 #include <brillo/flag_helper.h>
35 #include <brillo/key_value_store.h>
36 
37 #include "update_engine/client.h"
38 #include "update_engine/common/error_code.h"
39 #include "update_engine/common/error_code_utils.h"
40 #include "update_engine/cros/omaha_utils.h"
41 #include "update_engine/status_update_handler.h"
42 #include "update_engine/update_status.h"
43 #include "update_engine/update_status_utils.h"
44 
45 using brillo::KeyValueStore;
46 using chromeos_update_engine::EolDate;
47 using chromeos_update_engine::EolDateToString;
48 using chromeos_update_engine::ErrorCode;
49 using chromeos_update_engine::UpdateEngineStatusToString;
50 using chromeos_update_engine::UpdateStatusToString;
51 using chromeos_update_engine::utils::ErrorCodeToString;
52 using std::string;
53 using std::unique_ptr;
54 using std::vector;
55 using update_engine::UpdateEngineStatus;
56 using update_engine::UpdateStatus;
57 
58 namespace {
59 
60 // Constant to signal that we need to continue running the daemon after
61 // initialization.
62 const int kContinueRunning = -1;
63 
64 // The ShowStatus request will be retried `kShowStatusRetryCount` times at
65 // `kShowStatusRetryInterval` second intervals on failure.
66 const int kShowStatusRetryCount = 30;
67 const int kShowStatusRetryIntervalInSeconds = 2;
68 
69 class UpdateEngineClient : public brillo::Daemon {
70  public:
UpdateEngineClient(int argc,char ** argv)71   UpdateEngineClient(int argc, char** argv) : argc_(argc), argv_(argv) {}
72 
73   ~UpdateEngineClient() override = default;
74 
75  protected:
OnInit()76   int OnInit() override {
77     int ret = Daemon::OnInit();
78     if (ret != EX_OK)
79       return ret;
80 
81     client_ = update_engine::UpdateEngineClient::CreateInstance();
82 
83     if (!client_) {
84       LOG(ERROR) << "UpdateEngineService not available.";
85       return 1;
86     }
87 
88     // We can't call QuitWithExitCode from OnInit(), so we delay the execution
89     // of the ProcessFlags method after the Daemon initialization is done.
90     base::ThreadTaskRunnerHandle::Get()->PostTask(
91         FROM_HERE,
92         base::Bind(&UpdateEngineClient::ProcessFlagsAndExit,
93                    base::Unretained(this)));
94     return EX_OK;
95   }
96 
97  private:
98   // Show the status of the update engine in stdout.
99   bool ShowStatus();
100 
101   // Return whether we need to reboot. 0 if reboot is needed, 1 if an error
102   // occurred, 2 if no reboot is needed.
103   int GetNeedReboot();
104 
105   // Main method that parses and triggers all the actions based on the passed
106   // flags. Returns the exit code of the program of kContinueRunning if it
107   // should not exit.
108   int ProcessFlags();
109 
110   // Processes the flags and exits the program accordingly.
111   void ProcessFlagsAndExit();
112 
113   // Copy of argc and argv passed to main().
114   int argc_;
115   char** argv_;
116 
117   // Library-based client
118   unique_ptr<update_engine::UpdateEngineClient> client_;
119 
120   // Pointers to handlers for cleanup
121   vector<unique_ptr<update_engine::StatusUpdateHandler>> handlers_;
122 
123   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClient);
124 };
125 
126 class ExitingStatusUpdateHandler : public update_engine::StatusUpdateHandler {
127  public:
128   ~ExitingStatusUpdateHandler() override = default;
129 
130   void IPCError(const string& error) override;
131 };
132 
IPCError(const string & error)133 void ExitingStatusUpdateHandler::IPCError(const string& error) {
134   LOG(ERROR) << error;
135   exit(1);
136 }
137 
138 class WatchingStatusUpdateHandler : public ExitingStatusUpdateHandler {
139  public:
140   ~WatchingStatusUpdateHandler() override = default;
141 
142   void HandleStatusUpdate(const UpdateEngineStatus& status) override;
143 };
144 
HandleStatusUpdate(const UpdateEngineStatus & status)145 void WatchingStatusUpdateHandler::HandleStatusUpdate(
146     const UpdateEngineStatus& status) {
147   LOG(INFO) << "Got status update: " << UpdateEngineStatusToString(status);
148 }
149 
ShowStatus()150 bool UpdateEngineClient::ShowStatus() {
151   UpdateEngineStatus status;
152   int retry_count = kShowStatusRetryCount;
153   while (retry_count > 0) {
154     if (client_->GetStatus(&status)) {
155       break;
156     }
157     if (--retry_count == 0) {
158       return false;
159     }
160     LOG(WARNING)
161         << "Failed to get the update_engine status. This can happen when the"
162            " update_engine is busy doing a heavy operation or if the"
163            " update-engine service is down. If it doesn't resolve, a restart of"
164            " the update-engine service is needed."
165            " Will try "
166         << retry_count << " more times!";
167     base::PlatformThread::Sleep(
168         base::TimeDelta::FromSeconds(kShowStatusRetryIntervalInSeconds));
169   }
170 
171   printf("%s", UpdateEngineStatusToString(status).c_str());
172 
173   return true;
174 }
175 
GetNeedReboot()176 int UpdateEngineClient::GetNeedReboot() {
177   UpdateEngineStatus status;
178   if (!client_->GetStatus(&status)) {
179     return 1;
180   }
181 
182   if (status.status == UpdateStatus::UPDATED_NEED_REBOOT) {
183     return 0;
184   }
185 
186   return 2;
187 }
188 
189 class UpdateWaitHandler : public ExitingStatusUpdateHandler {
190  public:
UpdateWaitHandler(bool exit_on_error,update_engine::UpdateEngineClient * client)191   explicit UpdateWaitHandler(bool exit_on_error,
192                              update_engine::UpdateEngineClient* client)
193       : exit_on_error_(exit_on_error), client_(client) {}
194 
195   ~UpdateWaitHandler() override = default;
196 
197   void HandleStatusUpdate(const UpdateEngineStatus& status) override;
198 
199  private:
200   bool exit_on_error_;
201   update_engine::UpdateEngineClient* client_;
202 };
203 
HandleStatusUpdate(const UpdateEngineStatus & status)204 void UpdateWaitHandler::HandleStatusUpdate(const UpdateEngineStatus& status) {
205   if (exit_on_error_ && status.status == UpdateStatus::IDLE) {
206     int last_attempt_error = static_cast<int>(ErrorCode::kSuccess);
207     ErrorCode code = ErrorCode::kSuccess;
208     if (client_ && client_->GetLastAttemptError(&last_attempt_error))
209       code = static_cast<ErrorCode>(last_attempt_error);
210 
211     LOG(ERROR) << "Update failed, current operation is "
212                << UpdateStatusToString(status.status) << ", last error code is "
213                << ErrorCodeToString(code) << "(" << last_attempt_error << ")";
214     exit(1);
215   }
216   if (status.status == UpdateStatus::UPDATED_NEED_REBOOT) {
217     LOG(INFO) << "Update succeeded -- reboot needed.";
218     exit(0);
219   }
220 }
221 
ProcessFlags()222 int UpdateEngineClient::ProcessFlags() {
223   DEFINE_string(app_version, "", "Force the current app version.");
224   DEFINE_string(channel,
225                 "",
226                 "Set the target channel. The device will be powerwashed if the "
227                 "target channel is more stable than the current channel unless "
228                 "--nopowerwash is specified.");
229   DEFINE_bool(check_for_update, false, "Initiate check for updates.");
230   DEFINE_string(
231       cohort_hint, "", "Set the current cohort hint to the passed value.");
232   DEFINE_bool(follow,
233               false,
234               "Wait for any update operations to complete."
235               "Exit status is 0 if the update succeeded, and 1 otherwise.");
236   DEFINE_bool(interactive, true, "Mark the update request as interactive.");
237   DEFINE_string(omaha_url, "", "The URL of the Omaha update server.");
238   DEFINE_string(p2p_update,
239                 "",
240                 "Enables (\"yes\") or disables (\"no\") the peer-to-peer update"
241                 " sharing.");
242   DEFINE_bool(powerwash,
243               true,
244               "When performing rollback or channel change, "
245               "do a powerwash or allow it respectively.");
246   DEFINE_bool(reboot, false, "Initiate a reboot if needed.");
247   DEFINE_bool(is_reboot_needed,
248               false,
249               "Exit status 0 if reboot is needed, "
250               "2 if reboot is not needed or 1 if an error occurred.");
251   DEFINE_bool(block_until_reboot_is_needed,
252               false,
253               "Blocks until reboot is "
254               "needed. Returns non-zero exit status if an error occurred.");
255   DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle.");
256   DEFINE_bool(rollback,
257               false,
258               "Perform a rollback to the previous partition. The device will "
259               "be powerwashed unless --nopowerwash is specified.");
260   DEFINE_bool(can_rollback,
261               false,
262               "Shows whether rollback partition "
263               "is available.");
264   DEFINE_bool(show_channel, false, "Show the current and target channels.");
265   DEFINE_bool(show_cohort_hint, false, "Show the current cohort hint.");
266   DEFINE_bool(show_p2p_update,
267               false,
268               "Show the current setting for peer-to-peer update sharing.");
269   DEFINE_bool(show_update_over_cellular,
270               false,
271               "Show the current setting for updates over cellular networks.");
272   DEFINE_bool(status, false, "Print the status to stdout.");
273   DEFINE_bool(update,
274               false,
275               "Forces an update and waits for it to complete. "
276               "Implies --follow.");
277   DEFINE_string(update_over_cellular,
278                 "",
279                 "Enables (\"yes\") or disables (\"no\") the updates over "
280                 "cellular networks.");
281   DEFINE_bool(watch_for_updates,
282               false,
283               "Listen for status updates and print them to the screen.");
284   DEFINE_bool(prev_version,
285               false,
286               "Show the previous OS version used before the update reboot.");
287   DEFINE_bool(last_attempt_error, false, "Show the last attempt error.");
288   DEFINE_bool(eol_status, false, "Show the current end-of-life status.");
289 
290   // Boilerplate init commands.
291   base::CommandLine::Init(argc_, argv_);
292   brillo::FlagHelper::Init(argc_, argv_, "A/B Update Engine Client");
293 
294   // Ensure there are no positional arguments.
295   const vector<string> positional_args =
296       base::CommandLine::ForCurrentProcess()->GetArgs();
297   if (!positional_args.empty()) {
298     LOG(ERROR) << "Found a positional argument '" << positional_args.front()
299                << "'. If you want to pass a value to a flag, pass it as "
300                   "--flag=value.";
301     return 1;
302   }
303 
304   // Update the status if requested.
305   if (FLAGS_reset_status) {
306     LOG(INFO) << "Setting Update Engine status to idle ...";
307 
308     if (client_->ResetStatus()) {
309       LOG(INFO) << "ResetStatus succeeded; to undo partition table changes "
310                    "run:\n"
311                    "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo "
312                    "${P#$D} | sed 's/^[^0-9]*//')-1)) $D;)";
313     } else {
314       LOG(ERROR) << "ResetStatus failed";
315       return 1;
316     }
317   }
318 
319   // Changes the current update over cellular network setting.
320   if (!FLAGS_update_over_cellular.empty()) {
321     bool allowed = FLAGS_update_over_cellular == "yes";
322     if (!allowed && FLAGS_update_over_cellular != "no") {
323       LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular
324                  << "\". Please specify \"yes\" or \"no\".";
325     } else {
326       if (!client_->SetUpdateOverCellularPermission(allowed)) {
327         LOG(ERROR) << "Error setting the update over cellular setting.";
328         return 1;
329       }
330     }
331   }
332 
333   // Show the current update over cellular network setting.
334   if (FLAGS_show_update_over_cellular) {
335     bool allowed;
336 
337     if (!client_->GetUpdateOverCellularPermission(&allowed)) {
338       LOG(ERROR) << "Error getting the update over cellular setting.";
339       return 1;
340     }
341 
342     LOG(INFO) << "Current update over cellular network setting: "
343               << (allowed ? "ENABLED" : "DISABLED");
344   }
345 
346   // Change/show the cohort hint.
347   bool set_cohort_hint =
348       base::CommandLine::ForCurrentProcess()->HasSwitch("cohort_hint");
349   if (set_cohort_hint) {
350     LOG(INFO) << "Setting cohort hint to: \"" << FLAGS_cohort_hint << "\"";
351     if (!client_->SetCohortHint(FLAGS_cohort_hint)) {
352       LOG(ERROR) << "Error setting the cohort hint.";
353       return 1;
354     }
355   }
356 
357   if (FLAGS_show_cohort_hint || set_cohort_hint) {
358     string cohort_hint;
359     if (!client_->GetCohortHint(&cohort_hint)) {
360       LOG(ERROR) << "Error getting the cohort hint.";
361       return 1;
362     }
363 
364     LOG(INFO) << "Current cohort hint: \"" << cohort_hint << "\"";
365   }
366 
367   if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) {
368     LOG(ERROR) << "powerwash flag only makes sense rollback or channel change";
369     return 1;
370   }
371 
372   // Change the P2P enabled setting.
373   if (!FLAGS_p2p_update.empty()) {
374     bool enabled = FLAGS_p2p_update == "yes";
375     if (!enabled && FLAGS_p2p_update != "no") {
376       LOG(ERROR) << "Unknown option: \"" << FLAGS_p2p_update
377                  << "\". Please specify \"yes\" or \"no\".";
378     } else {
379       if (!client_->SetP2PUpdatePermission(enabled)) {
380         LOG(ERROR) << "Error setting the peer-to-peer update setting.";
381         return 1;
382       }
383     }
384   }
385 
386   // Show the rollback availability.
387   if (FLAGS_can_rollback) {
388     string rollback_partition;
389 
390     if (!client_->GetRollbackPartition(&rollback_partition)) {
391       LOG(ERROR) << "Error while querying rollback partition availability.";
392       return 1;
393     }
394 
395     bool can_rollback = true;
396     if (rollback_partition.empty()) {
397       rollback_partition = "UNAVAILABLE";
398       can_rollback = false;
399     } else {
400       rollback_partition = "AVAILABLE: " + rollback_partition;
401     }
402 
403     LOG(INFO) << "Rollback partition: " << rollback_partition;
404     if (!can_rollback) {
405       return 1;
406     }
407   }
408 
409   // Show the current P2P enabled setting.
410   if (FLAGS_show_p2p_update) {
411     bool enabled;
412 
413     if (!client_->GetP2PUpdatePermission(&enabled)) {
414       LOG(ERROR) << "Error getting the peer-to-peer update setting.";
415       return 1;
416     }
417 
418     LOG(INFO) << "Current update using P2P setting: "
419               << (enabled ? "ENABLED" : "DISABLED");
420   }
421 
422   // First, update the target channel if requested.
423   if (!FLAGS_channel.empty()) {
424     if (!client_->SetTargetChannel(FLAGS_channel, FLAGS_powerwash)) {
425       LOG(ERROR) << "Error setting the channel.";
426       return 1;
427     }
428 
429     LOG(INFO) << "Channel permanently set to: " << FLAGS_channel;
430   }
431 
432   // Show the current and target channels if requested.
433   if (FLAGS_show_channel) {
434     string current_channel;
435     string target_channel;
436 
437     if (!client_->GetChannel(&current_channel)) {
438       LOG(ERROR) << "Error getting the current channel.";
439       return 1;
440     }
441 
442     if (!client_->GetTargetChannel(&target_channel)) {
443       LOG(ERROR) << "Error getting the target channel.";
444       return 1;
445     }
446 
447     LOG(INFO) << "Current Channel: " << current_channel;
448 
449     if (!target_channel.empty())
450       LOG(INFO) << "Target Channel (pending update): " << target_channel;
451   }
452 
453   bool do_update_request = FLAGS_check_for_update || FLAGS_update ||
454                            !FLAGS_app_version.empty() ||
455                            !FLAGS_omaha_url.empty();
456   if (FLAGS_update)
457     FLAGS_follow = true;
458 
459   if (do_update_request && FLAGS_rollback) {
460     LOG(ERROR) << "Incompatible flags specified with rollback."
461                << "Rollback should not include update-related flags.";
462     return 1;
463   }
464 
465   if (FLAGS_rollback) {
466     LOG(INFO) << "Requesting rollback.";
467     if (!client_->Rollback(FLAGS_powerwash)) {
468       LOG(ERROR) << "Rollback request failed.";
469       return 1;
470     }
471   }
472 
473   // Initiate an update check, if necessary.
474   if (do_update_request) {
475     LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
476     string app_version = FLAGS_app_version;
477     if (FLAGS_update && app_version.empty()) {
478       app_version = "ForcedUpdate";
479       LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate.";
480     }
481     LOG(INFO) << "Initiating update check.";
482     if (!client_->AttemptUpdate(
483             app_version, FLAGS_omaha_url, FLAGS_interactive)) {
484       LOG(ERROR) << "Error checking for update.";
485       return 1;
486     }
487   }
488 
489   // These final options are all mutually exclusive with one another.
490   if (FLAGS_follow + FLAGS_watch_for_updates + FLAGS_reboot + FLAGS_status +
491           FLAGS_is_reboot_needed + FLAGS_block_until_reboot_is_needed >
492       1) {
493     LOG(ERROR) << "Multiple exclusive options selected. "
494                << "Select only one of --follow, --watch_for_updates, --reboot, "
495                << "--is_reboot_needed, --block_until_reboot_is_needed, "
496                << "or --status.";
497     return 1;
498   }
499 
500   if (FLAGS_status) {
501     LOG(INFO) << "Querying Update Engine status...";
502     if (!ShowStatus()) {
503       LOG(ERROR) << "Failed to query status";
504       return 1;
505     }
506     return 0;
507   }
508 
509   if (FLAGS_follow) {
510     LOG(INFO) << "Waiting for update to complete.";
511     auto handler = new UpdateWaitHandler(true, client_.get());
512     handlers_.emplace_back(handler);
513     client_->RegisterStatusUpdateHandler(handler);
514     return kContinueRunning;
515   }
516 
517   if (FLAGS_watch_for_updates) {
518     LOG(INFO) << "Watching for status updates.";
519     auto handler = new WatchingStatusUpdateHandler();
520     handlers_.emplace_back(handler);
521     client_->RegisterStatusUpdateHandler(handler);
522     return kContinueRunning;
523   }
524 
525   if (FLAGS_reboot) {
526     LOG(INFO) << "Requesting a reboot...";
527     client_->RebootIfNeeded();
528     return 0;
529   }
530 
531   if (FLAGS_prev_version) {
532     string prev_version;
533 
534     if (!client_->GetPrevVersion(&prev_version)) {
535       LOG(ERROR) << "Error getting previous version.";
536     } else {
537       LOG(INFO) << "Previous version = " << prev_version;
538     }
539   }
540 
541   if (FLAGS_is_reboot_needed) {
542     int ret = GetNeedReboot();
543 
544     if (ret == 1) {
545       LOG(ERROR) << "Could not query the current operation.";
546     }
547 
548     return ret;
549   }
550 
551   if (FLAGS_block_until_reboot_is_needed) {
552     auto handler = new UpdateWaitHandler(false, nullptr);
553     handlers_.emplace_back(handler);
554     client_->RegisterStatusUpdateHandler(handler);
555     return kContinueRunning;
556   }
557 
558   if (FLAGS_last_attempt_error) {
559     int last_attempt_error;
560     if (!client_->GetLastAttemptError(&last_attempt_error)) {
561       LOG(ERROR) << "Error getting last attempt error.";
562     } else {
563       ErrorCode code = static_cast<ErrorCode>(last_attempt_error);
564 
565       KeyValueStore last_attempt_error_store;
566       last_attempt_error_store.SetString(
567           "ERROR_CODE", base::NumberToString(last_attempt_error));
568       last_attempt_error_store.SetString("ERROR_MESSAGE",
569                                          ErrorCodeToString(code));
570       printf("%s", last_attempt_error_store.SaveToString().c_str());
571     }
572   }
573 
574   if (FLAGS_eol_status) {
575     UpdateEngineStatus status;
576     if (!client_->GetStatus(&status)) {
577       LOG(ERROR) << "Error GetStatus() for getting EOL info.";
578     } else {
579       EolDate eol_date_code = status.eol_date;
580 
581       KeyValueStore eol_status_store;
582       eol_status_store.SetString("EOL_DATE", EolDateToString(eol_date_code));
583       printf("%s", eol_status_store.SaveToString().c_str());
584     }
585   }
586 
587   return 0;
588 }
589 
ProcessFlagsAndExit()590 void UpdateEngineClient::ProcessFlagsAndExit() {
591   int ret = ProcessFlags();
592   if (ret != kContinueRunning)
593     QuitWithExitCode(ret);
594 }
595 
596 }  // namespace
597 
main(int argc,char ** argv)598 int main(int argc, char** argv) {
599   UpdateEngineClient client(argc, argv);
600   return client.Run();
601 }
602