xref: /haiku/src/apps/haikudepot/packagemanagement/InstallPackageProcess.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
1 /*
2  * Copyright 2013-2021, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold <ingo_weinhold@gmx.de>
7  * 		Stephan Aßmus <superstippi@gmx.de>
8  * 		Rene Gollent <rene@gollent.com>
9  *		Julian Harnath <julian.harnath@rwth-aachen.de>
10  *		Andrew Lindesay <apl@lindesay.co.nz>
11  *
12  * Note that this file has been re-factored from `PackageManager.cpp` and
13  * authors have been carried across in 2021.
14  */
15 
16 
17 #include "InstallPackageProcess.h"
18 
19 #include <algorithm>
20 
21 #include <AutoLocker.h>
22 #include <Catalog.h>
23 #include <Locker.h>
24 
25 #include <package/manager/Exceptions.h>
26 #include <package/hpkg/NoErrorOutput.h>
27 #include <package/hpkg/PackageContentHandler.h>
28 #include <package/hpkg/PackageEntry.h>
29 #include <package/hpkg/PackageEntryAttribute.h>
30 #include <package/hpkg/PackageReader.h>
31 #include <package/solver/SolverPackage.h>
32 
33 #include "AppUtils.h"
34 #include "Logger.h"
35 #include "PackageManager.h"
36 
37 
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "InstallPackageProcess"
40 
41 using namespace BPackageKit;
42 using namespace BPackageKit::BPrivate;
43 using namespace BPackageKit::BManager::BPrivate;
44 
45 using BPackageKit::BSolver;
46 using BPackageKit::BSolverPackage;
47 using BPackageKit::BSolverRepository;
48 using BPackageKit::BHPKG::BNoErrorOutput;
49 using BPackageKit::BHPKG::BPackageContentHandler;
50 using BPackageKit::BHPKG::BPackageEntry;
51 using BPackageKit::BHPKG::BPackageEntryAttribute;
52 using BPackageKit::BHPKG::BPackageInfoAttributeValue;
53 using BPackageKit::BHPKG::BPackageReader;
54 
55 
56 class DownloadProgress {
57 public:
58 	DownloadProgress(BString packageName, float progress)
59 		:
60 		fPackageName(packageName),
61 		fProgress(progress)
62 	{
63 	}
64 
65 	virtual ~DownloadProgress()
66 	{
67 	}
68 
69 	BString PackageName() const
70 	{
71 		return fPackageName;
72 	}
73 
74 	float Progress() const
75 	{
76 		return fProgress;
77 	}
78 
79 private:
80 	BString	fPackageName;
81 	float	fProgress;
82 };
83 
84 
85 InstallPackageProcess::InstallPackageProcess(
86 	PackageInfoRef package, Model* model)
87 	:
88 	AbstractPackageProcess(package, model),
89 	fLastDownloadUpdate(0)
90 {
91 	fDescription = B_TRANSLATE("Installing \"%PackageName%\"");
92 	fDescription.ReplaceAll("%PackageName%", package->Name());
93 }
94 
95 
96 InstallPackageProcess::~InstallPackageProcess()
97 {
98 }
99 
100 
101 const char*
102 InstallPackageProcess::Name() const
103 {
104 	return "InstallPackageProcess";
105 }
106 
107 
108 const char*
109 InstallPackageProcess::Description() const
110 {
111 	return fDescription;
112 }
113 
114 
115 float
116 InstallPackageProcess::Progress()
117 {
118 	if (ProcessState() == PROCESS_RUNNING && !fDownloadProgresses.empty()) {
119 		AutoLocker<BLocker> locker(&fLock);
120 		float sum = 0.0;
121 		std::vector<DownloadProgress>::const_iterator it;
122 		for (it = fDownloadProgresses.begin();
123 				it != fDownloadProgresses.end(); it++) {
124 			DownloadProgress downloadProgress = *it;
125 			sum += downloadProgress.Progress();
126 		}
127 		if (sum > 0.0)
128 			return sum / (float) fDownloadProgresses.size();
129 	}
130 	return kProgressIndeterminate;
131 }
132 
133 
134 status_t
135 InstallPackageProcess::RunInternal()
136 {
137 	fPackageManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES
138 		| BPackageManager::B_ADD_REMOTE_REPOSITORIES
139 		| BPackageManager::B_REFRESH_REPOSITORIES);
140 	PackageInfoRef ref(fPackage);
141 	PackageState state = ref->State();
142 	ref->SetState(PENDING);
143 
144 	fPackageManager->SetCurrentActionPackage(ref, true);
145 	fPackageManager->AddProgressListener(this);
146 
147 	BString packageName;
148 	if (ref->IsLocalFile())
149 		packageName = ref->LocalFilePath();
150 	else
151 		packageName = ref->Name();
152 
153 	const char* packageNameString = packageName.String();
154 	try {
155 		fPackageManager->Install(&packageNameString, 1);
156 	} catch (BFatalErrorException& ex) {
157 		BString errorString;
158 		errorString.SetToFormat(
159 			"Fatal error occurred while installing package %s: "
160 			"%s (%s)\n", packageNameString, ex.Message().String(),
161 			ex.Details().String());
162 		AppUtils::NotifySimpleError(B_TRANSLATE("Fatal error"), errorString,
163 			B_STOP_ALERT);
164 		_SetDownloadedPackagesState(NONE);
165 		ref->SetState(state);
166 		return ex.Error();
167 	} catch (BAbortedByUserException& ex) {
168 		HDINFO("Installation of package %s is aborted by user: %s",
169 			packageNameString, ex.Message().String());
170 		_SetDownloadedPackagesState(NONE);
171 		ref->SetState(state);
172 		return B_OK;
173 	} catch (BNothingToDoException& ex) {
174 		HDINFO("Nothing to do while installing package %s: %s",
175 			packageNameString, ex.Message().String());
176 		return B_OK;
177 	} catch (BException& ex) {
178 		HDERROR("Exception occurred while installing package %s: %s",
179 			packageNameString, ex.Message().String());
180 		_SetDownloadedPackagesState(NONE);
181 		ref->SetState(state);
182 		return B_ERROR;
183 	}
184 
185 	fPackageManager->RemoveProgressListener(this);
186 
187 	_SetDownloadedPackagesState(ACTIVATED);
188 
189 	return B_OK;
190 }
191 
192 
193 // #pragma mark - DownloadProgressListener
194 
195 
196 void
197 InstallPackageProcess::DownloadProgressChanged(
198 	const char* packageName, float progress)
199 {
200 	bigtime_t now = system_time();
201 	if (now - fLastDownloadUpdate < 250000 && progress != 1.0)
202 		return;
203 	fLastDownloadUpdate = now;
204 	BString simplePackageName;
205 	if (_DeriveSimplePackageName(packageName, simplePackageName) != B_OK) {
206 		HDERROR("malformed canonical package name [%s]", packageName);
207 		return;
208 	}
209 	PackageInfoRef ref(FindPackageByName(simplePackageName));
210 	if (ref.IsSet()) {
211 		ref->SetDownloadProgress(progress);
212 		_SetDownloadProgress(simplePackageName, progress);
213 	} else {
214 		HDERROR("unable to find the package info for simple package name [%s]",
215 			simplePackageName.String());
216 	}
217 }
218 
219 
220 void
221 InstallPackageProcess::DownloadProgressComplete(const char* packageName)
222 {
223 	BString simplePackageName;
224 	if (_DeriveSimplePackageName(packageName, simplePackageName) != B_OK) {
225 		HDERROR("malformed canonical package name [%s]", packageName);
226 		return;
227 	}
228 	_SetDownloadProgress(simplePackageName, 1.0);
229 	PackageInfoRef ref(FindPackageByName(simplePackageName));
230 	if (!ref.IsSet()) {
231 		HDERROR("unable to find the package info for simple package name [%s]",
232 			simplePackageName.String());
233 		return;
234 	}
235 	ref->SetDownloadProgress(1.0);
236 	fDownloadedPackages.insert(ref);
237 }
238 
239 
240 void
241 InstallPackageProcess::ConfirmedChanges(
242 	BPackageManager::InstalledRepository& repository)
243 {
244 	BPackageManager::InstalledRepository::PackageList& activationList =
245 		repository.PackagesToActivate();
246 
247 	BSolverPackage* package = NULL;
248 	for (int32 i = 0; (package = activationList.ItemAt(i)); i++) {
249 		PackageInfoRef ref(FindPackageByName(package->Info().Name()));
250 		if (ref.IsSet())
251 			ref->SetState(PENDING);
252 	}
253 }
254 
255 
256 void
257 InstallPackageProcess::_SetDownloadedPackagesState(PackageState state)
258 {
259 	for (PackageInfoSet::iterator it = fDownloadedPackages.begin();
260 			it != fDownloadedPackages.end(); ++it) {
261 		(*it)->SetState(state);
262 	}
263 }
264 
265 
266 static bool
267 _IsDownloadProgressBefore(const DownloadProgress& dp1,
268 	const DownloadProgress& dp2)
269 {
270 	return dp1.PackageName().Compare(dp2.PackageName()) < 0;
271 }
272 
273 
274 /*!	This method will extract the plain package name from the canonical
275  */
276 
277 /*static*/ status_t
278 InstallPackageProcess::_DeriveSimplePackageName(const BString& canonicalForm,
279 	BString& simplePackageName)
280 {
281 	int32 hypenIndex = canonicalForm.FindFirst('-');
282 	if (hypenIndex <= 0)
283 		return B_BAD_DATA;
284 	simplePackageName.SetTo(canonicalForm);
285 	simplePackageName.Truncate(hypenIndex);
286 	return B_OK;
287 }
288 
289 
290 void
291 InstallPackageProcess::_SetDownloadProgress(const BString& simplePackageName,
292 	float progress)
293 {
294 	AutoLocker<BLocker> locker(&fLock);
295 	DownloadProgress downloadProgress(simplePackageName, progress);
296 	std::vector<DownloadProgress>::iterator itInsertionPt
297 		= std::lower_bound(
298 			fDownloadProgresses.begin(), fDownloadProgresses.end(),
299 			downloadProgress, &_IsDownloadProgressBefore);
300 
301 	if (itInsertionPt != fDownloadProgresses.end()) {
302 		if ((*itInsertionPt).PackageName() == simplePackageName) {
303 			itInsertionPt = fDownloadProgresses.erase(itInsertionPt);
304 		}
305 	}
306 
307 	fDownloadProgresses.insert(itInsertionPt, downloadProgress);
308 	_NotifyChanged();
309 }
310