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