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