blob: 7f16cfa8431f65ff5b8dc51e0df6aca94d7e80c0 [file] [log] [blame]
Jae Hoon Kim823ead32019-12-13 09:30:09 -08001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Amin Hassani04fda432020-02-21 11:31:14 -08005#include "dlcservice/dlc_manager.h"
6
Jae Hoon Kimc98e3a32020-03-13 09:40:48 -07007#include <cinttypes>
Jae Hoon Kim823ead32019-12-13 09:30:09 -08008#include <utility>
9
Qijiang Fan713061e2021-03-08 15:45:12 +090010#include <base/check.h>
Jae Hoon Kim823ead32019-12-13 09:30:09 -080011#include <base/strings/stringprintf.h>
Jae Hoon Kim823ead32019-12-13 09:30:09 -080012#include <chromeos/dbus/service_constants.h>
13#include <dbus/dlcservice/dbus-constants.h>
Amin Hassania69f32e2020-03-30 15:20:42 -070014#include <dlcservice/proto_bindings/dlcservice.pb.h>
Jae Hoon Kim823ead32019-12-13 09:30:09 -080015
Amin Hassania69f32e2020-03-30 15:20:42 -070016#include "dlcservice/dlc.h"
Jae Hoon Kimc98e3a32020-03-13 09:40:48 -070017#include "dlcservice/error.h"
Jae Hoon Kimf4eed882020-01-28 17:29:16 -080018#include "dlcservice/system_state.h"
Jae Hoon Kim823ead32019-12-13 09:30:09 -080019#include "dlcservice/utils.h"
20
Jae Hoon Kimc98e3a32020-03-13 09:40:48 -070021using brillo::ErrorPtr;
Amin Hassani40c0f112020-04-15 09:55:00 -070022using brillo::MessageLoop;
Jae Hoon Kim823ead32019-12-13 09:30:09 -080023
24namespace dlcservice {
25
Amin Hassanid421b412020-07-17 17:37:30 -070026namespace {
27DlcIdList ToDlcIdList(const DlcMap& dlcs,
28 const std::function<bool(const DlcBase&)>& filter) {
29 DlcIdList list;
30 for (const auto& pair : dlcs) {
31 if (filter(pair.second))
32 list.push_back(pair.first);
33 }
34 return list;
35}
36} // namespace
37
Amin Hassani40c0f112020-04-15 09:55:00 -070038DlcManager::~DlcManager() {
39 if (cleanup_dangling_task_id_ != MessageLoop::kTaskIdNull) {
40 MessageLoop::current()->CancelTask(cleanup_dangling_task_id_);
41 cleanup_dangling_task_id_ = MessageLoop::kTaskIdNull;
42 }
43}
44
Amin Hassanica3cbb72020-04-10 12:26:50 -070045void DlcManager::Initialize() {
Amin Hassani40c0f112020-04-15 09:55:00 -070046 supported_.clear();
47
Amin Hassani7373abd2020-03-27 12:56:37 -070048 // Initialize supported DLC(s).
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -070049 for (const auto& id : ScanDirectory(SystemState::Get()->manifest_dir())) {
Amin Hassania69f32e2020-03-30 15:20:42 -070050 auto result = supported_.emplace(id, id);
51 if (!result.first->second.Initialize()) {
52 LOG(ERROR) << "Failed to initialize DLC " << id;
53 supported_.erase(id);
54 }
55 }
Amin Hassani40c0f112020-04-15 09:55:00 -070056
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -070057 CleanupUnsupportedDlcs();
Amin Hassani40c0f112020-04-15 09:55:00 -070058
59 // Post cleaning up dangling Dlcs for after the user has worked on the device
60 // for a bit in case they install one of the dangling DLCs.
61 constexpr int kTimeoutMinutes = 30;
62 PostCleanupDanglingDlcs(base::TimeDelta::FromMinutes(kTimeoutMinutes));
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -070063}
Jae Hoon Kim823ead32019-12-13 09:30:09 -080064
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -070065void DlcManager::CleanupUnsupportedDlcs() {
Jae Hoon Kim6e3026d2020-07-10 11:04:29 -070066 auto* system_state = SystemState::Get();
Jae Hoon Kim0c2b6cd2020-04-30 13:23:16 -070067 // Delete deprecated DLC(s) in content directory.
68 for (const auto& id : ScanDirectory(system_state->content_dir())) {
Amin Hassani1ac28312020-06-04 18:16:30 -070069 brillo::ErrorPtr tmp_err;
70 if (GetDlc(id, &tmp_err) != nullptr)
Jae Hoon Kim0c2b6cd2020-04-30 13:23:16 -070071 continue;
72 for (const auto& path : DlcBase::GetPathsToDelete(id))
Andrew2673ee42020-05-08 15:27:10 -070073 if (base::PathExists(path)) {
hscham53cf73a2020-11-30 15:58:42 +090074 if (!base::DeletePathRecursively(path))
Andrew2673ee42020-05-08 15:27:10 -070075 PLOG(ERROR) << "Failed to delete path=" << path;
76 else
77 LOG(INFO) << "Deleted path=" << path << " for deprecated DLC=" << id;
78 }
Jae Hoon Kim0c2b6cd2020-04-30 13:23:16 -070079 }
80
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -070081 // Delete the unsupported/preload not allowed DLC(s) in the preloaded
82 // directory.
83 auto preloaded_content_dir = system_state->preloaded_content_dir();
84 for (const auto& id : ScanDirectory(preloaded_content_dir)) {
Amin Hassani1ac28312020-06-04 18:16:30 -070085 brillo::ErrorPtr tmp_err;
86 auto* dlc = GetDlc(id, &tmp_err);
87 if (dlc != nullptr && dlc->IsPreloadAllowed())
88 continue;
89
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -070090 // Preloading is not allowed for this image so it will be deleted.
91 auto path = JoinPaths(preloaded_content_dir, id);
hscham53cf73a2020-11-30 15:58:42 +090092 if (!base::DeletePathRecursively(path))
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -070093 PLOG(ERROR) << "Failed to delete path=" << path;
94 else
95 LOG(INFO) << "Deleted path=" << path
96 << " for unsupported/preload not allowed DLC=" << id;
97 }
Amin Hassanica3cbb72020-04-10 12:26:50 -070098}
Amin Hassani1fd20822020-03-27 11:52:23 -070099
Amin Hassani40c0f112020-04-15 09:55:00 -0700100void DlcManager::CleanupDanglingDlcs() {
101 LOG(INFO) << "Going to clean up dangling DLCs.";
102 for (auto& pair : supported_) {
103 auto& dlc = pair.second;
104 if (dlc.ShouldPurge()) {
105 LOG(INFO) << "DLC=" << dlc.GetId() << " should be removed because it is "
106 << "dangling.";
107 brillo::ErrorPtr error;
108 if (!dlc.Purge(&error)) {
Andrew936ccf62020-06-12 13:00:23 -0700109 LOG(ERROR) << "Failed to delete dangling DLC=" << dlc.GetId();
Amin Hassani40c0f112020-04-15 09:55:00 -0700110 }
111 }
112 }
113
114 // Post another one to happen in a day in case they never shutdown their
115 // devices.
116 constexpr int kTimeoutDays = 1;
117 PostCleanupDanglingDlcs(base::TimeDelta::FromDays(kTimeoutDays));
118}
119
120void DlcManager::PostCleanupDanglingDlcs(const base::TimeDelta& timeout) {
121 cleanup_dangling_task_id_ = MessageLoop::current()->PostDelayedTask(
122 FROM_HERE,
Jae Hoon Kimc85824c2021-05-12 13:19:14 -0700123 base::BindOnce(&DlcManager::CleanupDanglingDlcs, base::Unretained(this)),
Amin Hassani40c0f112020-04-15 09:55:00 -0700124 timeout);
125}
126
Amin Hassani1ac28312020-06-04 18:16:30 -0700127DlcBase* DlcManager::GetDlc(const DlcId& id, brillo::ErrorPtr* err) {
Amin Hassani86649982020-03-31 16:03:37 -0700128 const auto& iter = supported_.find(id);
Amin Hassanib2f139c2020-05-15 11:43:26 -0700129 if (iter == supported_.end()) {
Amin Hassani1ac28312020-06-04 18:16:30 -0700130 *err = Error::Create(
131 FROM_HERE, kErrorInvalidDlc,
132 base::StringPrintf("Passed unsupported DLC=%s", id.c_str()));
Amin Hassanib2f139c2020-05-15 11:43:26 -0700133 return nullptr;
134 }
Amin Hassaniaa38c792020-04-06 15:52:44 -0700135 return &iter->second;
Amin Hassani86649982020-03-31 16:03:37 -0700136}
137
Amin Hassani9ca846f2020-04-17 12:41:01 -0700138DlcIdList DlcManager::GetInstalled() {
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700139 // TODO(kimjae): Once update_engine repeatedly calls into |GetInstalled()| for
140 // updating update, need to handle clearing differently.
Amin Hassani38f36792020-04-17 11:47:08 -0700141 return ToDlcIdList(supported_,
142 [](const DlcBase& dlc) { return dlc.IsInstalled(); });
143}
144
Amin Hassanid5fc8b22020-04-29 12:44:52 -0700145DlcIdList DlcManager::GetExistingDlcs() {
146 return ToDlcIdList(supported_,
147 [](const DlcBase& dlc) { return dlc.HasContent(); });
148}
149
Amin Hassani38f36792020-04-17 11:47:08 -0700150DlcIdList DlcManager::GetDlcsToUpdate() {
Amin Hassani48567772020-05-20 16:21:11 -0700151 return ToDlcIdList(
152 supported_, [](const DlcBase& dlc) { return dlc.MakeReadyForUpdate(); });
Jae Hoon Kim6cb439b2020-03-23 14:02:44 -0700153}
154
Amin Hassani9ca846f2020-04-17 12:41:01 -0700155DlcIdList DlcManager::GetSupported() {
156 return ToDlcIdList(supported_, [](const DlcBase&) { return true; });
Amin Hassania69f32e2020-03-30 15:20:42 -0700157}
158
Amin Hassani9ca846f2020-04-17 12:41:01 -0700159bool DlcManager::InstallCompleted(const DlcIdList& ids, brillo::ErrorPtr* err) {
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700160 DCHECK(err);
161 bool ret = true;
162 for (const auto& id : ids) {
Amin Hassani1ac28312020-06-04 18:16:30 -0700163 auto* dlc = GetDlc(id, err);
164 if (dlc == nullptr) {
Amin Hassanif27aac02020-04-23 21:56:26 -0700165 LOG(WARNING) << "Trying to complete installation for unsupported DLC="
166 << id;
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700167 ret = false;
Amin Hassani1ac28312020-06-04 18:16:30 -0700168 } else if (!dlc->InstallCompleted(err)) {
Andrew936ccf62020-06-12 13:00:23 -0700169 PLOG(WARNING) << "Failed to complete install.";
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700170 ret = false;
171 }
172 }
Amin Hassani1ac28312020-06-04 18:16:30 -0700173 // The returned error pertains to the last error happened. We probably don't
174 // need any accumulation of errors.
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700175 return ret;
176}
177
Amin Hassani9ca846f2020-04-17 12:41:01 -0700178bool DlcManager::UpdateCompleted(const DlcIdList& ids, brillo::ErrorPtr* err) {
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700179 DCHECK(err);
180 bool ret = true;
181 for (const auto& id : ids) {
Amin Hassani1ac28312020-06-04 18:16:30 -0700182 auto* dlc = GetDlc(id, err);
183 if (dlc == nullptr) {
Amin Hassanif27aac02020-04-23 21:56:26 -0700184 LOG(WARNING) << "Trying to complete update for unsupported DLC=" << id;
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700185 ret = false;
Amin Hassani1ac28312020-06-04 18:16:30 -0700186 } else if (!dlc->UpdateCompleted(err)) {
Andrew936ccf62020-06-12 13:00:23 -0700187 LOG(WARNING) << "Failed to complete update.";
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700188 ret = false;
189 }
190 }
Amin Hassani1ac28312020-06-04 18:16:30 -0700191 // The returned error pertains to the last error happened. We probably don't
192 // need any accumulation of errors.
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700193 return ret;
194}
195
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700196bool DlcManager::Install(const DlcId& id,
197 bool* external_install_needed,
198 ErrorPtr* err) {
Jae Hoon Kimc98e3a32020-03-13 09:40:48 -0700199 DCHECK(err);
Amin Hassani1ac28312020-06-04 18:16:30 -0700200 auto* dlc = GetDlc(id, err);
201 if (dlc == nullptr) {
Amin Hassani6d0367d2020-05-10 18:07:03 -0700202 return false;
Amin Hassania69f32e2020-03-30 15:20:42 -0700203 }
204
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700205 // If the DLC is being installed, nothing can be done anymore.
Amin Hassani1ac28312020-06-04 18:16:30 -0700206 if (dlc->IsInstalling()) {
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700207 return true;
208 }
209
210 // Otherwise proceed to install the DLC.
Amin Hassani1ac28312020-06-04 18:16:30 -0700211 if (!dlc->Install(err)) {
Andrew0a534ed2020-05-06 09:59:17 -0700212 Error::AddInternalTo(
213 err, FROM_HERE, error::kFailedInternal,
214 base::StringPrintf("Failed to initialize installation for DLC=%s",
215 id.c_str()));
Amin Hassani6d0367d2020-05-10 18:07:03 -0700216 return false;
Amin Hassania69f32e2020-03-30 15:20:42 -0700217 }
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700218
219 // If the DLC is now in installing state, it means it now needs update_engine
220 // installation.
Amin Hassani1ac28312020-06-04 18:16:30 -0700221 *external_install_needed = dlc->IsInstalling();
Amin Hassania69f32e2020-03-30 15:20:42 -0700222 return true;
Jae Hoon Kim823ead32019-12-13 09:30:09 -0800223}
224
Amin Hassani40c0f112020-04-15 09:55:00 -0700225bool DlcManager::Uninstall(const DlcId& id, ErrorPtr* err) {
Jae Hoon Kimc98e3a32020-03-13 09:40:48 -0700226 DCHECK(err);
Amin Hassani1ac28312020-06-04 18:16:30 -0700227 auto* dlc = GetDlc(id, err);
228 if (dlc == nullptr) {
Jae Hoon Kim823ead32019-12-13 09:30:09 -0800229 return false;
230 }
Amin Hassani1ac28312020-06-04 18:16:30 -0700231 return dlc->Uninstall(err);
Amin Hassani40c0f112020-04-15 09:55:00 -0700232}
233
234bool DlcManager::Purge(const DlcId& id, ErrorPtr* err) {
235 DCHECK(err);
Amin Hassani1ac28312020-06-04 18:16:30 -0700236 auto* dlc = GetDlc(id, err);
237 if (dlc == nullptr) {
Amin Hassani40c0f112020-04-15 09:55:00 -0700238 return false;
239 }
Amin Hassani1ac28312020-06-04 18:16:30 -0700240 return dlc->Purge(err);
Amin Hassania69f32e2020-03-30 15:20:42 -0700241}
242
Jae Hoon Kim53514af2020-07-27 11:50:26 -0700243bool DlcManager::FinishInstall(const DlcId& id, ErrorPtr* err) {
Amin Hassania69f32e2020-03-30 15:20:42 -0700244 DCHECK(err);
Jae Hoon Kim53514af2020-07-27 11:50:26 -0700245 auto dlc = GetDlc(id, err);
246 if (!dlc) {
247 *err = Error::Create(FROM_HERE, kErrorInvalidDlc,
248 "Finishing installation for invalid DLC.");
249 return false;
Jae Hoon Kimfdaa53a2020-03-13 11:28:36 -0700250 }
Jae Hoon Kim53514af2020-07-27 11:50:26 -0700251 if (!dlc->IsInstalling()) {
252 *err = Error::Create(
253 FROM_HERE, kErrorInternal,
254 "Finishing installation for a DLC that is not being installed.");
255 return false;
256 }
257 return dlc->FinishInstall(/*installed_by_ue=*/true, err);
Amin Hassania69f32e2020-03-30 15:20:42 -0700258}
259
Andrewbcc4bd82020-06-11 14:23:55 -0700260bool DlcManager::CancelInstall(const DlcId& id,
261 const ErrorPtr& err_in,
262 ErrorPtr* err) {
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700263 DCHECK(err);
Amin Hassani1ac28312020-06-04 18:16:30 -0700264 auto* dlc = GetDlc(id, err);
265 if (dlc == nullptr) {
Jae Hoon Kim53514af2020-07-27 11:50:26 -0700266 *err = Error::Create(FROM_HERE, kErrorInvalidDlc,
267 "Cancelling installation for invalid DLC.");
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700268 return false;
269 }
Andrewbcc4bd82020-06-11 14:23:55 -0700270 return !dlc->IsInstalling() || dlc->CancelInstall(err_in, err);
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700271}
272
Amin Hassani78a5ec82020-05-19 09:47:49 -0700273void DlcManager::ChangeProgress(double progress) {
274 for (auto& pr : supported_) {
275 auto& dlc = pr.second;
276 if (dlc.IsInstalling()) {
277 dlc.ChangeProgress(progress);
278 }
279 }
280}
281
Jae Hoon Kim823ead32019-12-13 09:30:09 -0800282} // namespace dlcservice