1 /*
2 * Copyright 2018-2023, Andrew Lindesay <apl@lindesay.co.nz>.
3 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
4 * Copyright 2013, Rene Gollent, rene@gollent.com.
5 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
6
7 * All rights reserved. Distributed under the terms of the MIT License.
8 *
9 * Note that this file included code earlier from `MainWindow.cpp` and
10 * copyrights have been latterly been carried across in 2021.
11 */
12
13
14 #include "LocalPkgDataLoadProcess.h"
15
16 #include <map>
17 #include <vector>
18
19 #include <AutoDeleter.h>
20 #include <AutoLocker.h>
21 #include <Autolock.h>
22 #include <Catalog.h>
23 #include <Roster.h>
24 #include <StringList.h>
25
26 #include "AppUtils.h"
27 #include "HaikuDepotConstants.h"
28 #include "Logger.h"
29 #include "PackageInfo.h"
30 #include "PackageManager.h"
31 #include "PackageUtils.h"
32
33 #include <package/Context.h>
34 #include <package/manager/Exceptions.h>
35 #include <package/manager/RepositoryBuilder.h>
36 #include <package/PackageRoster.h>
37 #include "package/RepositoryCache.h"
38 #include <package/RefreshRepositoryRequest.h>
39 #include <package/solver/SolverPackage.h>
40 #include <package/solver/SolverResult.h>
41
42
43 #undef B_TRANSLATION_CONTEXT
44 #define B_TRANSLATION_CONTEXT "LocalPkgDataLoadProcess"
45
46
47 using namespace BPackageKit;
48 using namespace BPackageKit::BManager::BPrivate;
49
50
51 typedef std::map<BString, PackageInfoRef> PackageInfoMap;
52
53
54 /*!
55 \param packageInfoListener is assigned to each package model object.
56 */
57
LocalPkgDataLoadProcess(PackageInfoListenerRef packageInfoListener,Model * model,bool force)58 LocalPkgDataLoadProcess::LocalPkgDataLoadProcess(
59 PackageInfoListenerRef packageInfoListener,
60 Model *model, bool force)
61 :
62 AbstractProcess(),
63 fModel(model),
64 fForce(force),
65 fPackageInfoListener(packageInfoListener)
66 {
67 }
68
69
~LocalPkgDataLoadProcess()70 LocalPkgDataLoadProcess::~LocalPkgDataLoadProcess()
71 {
72 }
73
74
75 const char*
Name() const76 LocalPkgDataLoadProcess::Name() const
77 {
78 return "LocalPkgDataLoadProcess";
79 }
80
81
82 const char*
Description() const83 LocalPkgDataLoadProcess::Description() const
84 {
85 return B_TRANSLATE("Reading repository data");
86 }
87
88
89 /*! The contents of this method implementation have been 'lifted and shifted'
90 from MainWindow.cpp in order that the logic fits into the background
91 loading processes. The code needs to be broken up into methods with some
92 sort of a state object carrying the state of the process. As part of this,
93 better error handling and error reporting would also be advantageous.
94 */
95
96 status_t
RunInternal()97 LocalPkgDataLoadProcess::RunInternal()
98 {
99 HDDEBUG("[%s] will refresh the package list", Name());
100 BPackageRoster roster;
101 BStringList repositoryNames;
102
103 status_t result = roster.GetRepositoryNames(repositoryNames);
104
105 if (result != B_OK)
106 return result;
107
108 std::vector<DepotInfoRef> depots(repositoryNames.CountStrings());
109 for (int32 i = 0; i < repositoryNames.CountStrings(); i++) {
110 const BString& repoName = repositoryNames.StringAt(i);
111 DepotInfoRef depotInfoRef = DepotInfoRef(
112 new(std::nothrow) DepotInfo(repoName), true);
113
114 if (!depotInfoRef.IsSet())
115 HDFATAL("unable to create new depot info - memory exhaustion");
116
117 BRepositoryConfig repoConfig;
118 status_t getRepositoryConfigStatus = roster.GetRepositoryConfig(
119 repoName, &repoConfig);
120
121 if (getRepositoryConfigStatus == B_OK) {
122 depotInfoRef->SetIdentifier(repoConfig.Identifier());
123 HDDEBUG("[%s] local repository [%s] identifier; [%s]",
124 Name(), repoName.String(), repoConfig.Identifier().String());
125 } else {
126 HDINFO("[%s] unable to obtain the repository config for local "
127 "repository '%s'; %s", Name(),
128 repoName.String(), strerror(getRepositoryConfigStatus));
129 }
130
131 depots[i] = depotInfoRef;
132 }
133
134 PackageManager manager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
135 try {
136 manager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES
137 | PackageManager::B_ADD_REMOTE_REPOSITORIES);
138 } catch (BException& ex) {
139 BString message(B_TRANSLATE("An error occurred while "
140 "initializing the package manager: %message%"));
141 message.ReplaceFirst("%message%", ex.Message());
142 _NotifyError(message.String());
143 return B_ERROR;
144 }
145
146 BObjectList<BSolverPackage> packages;
147 result = manager.Solver()->FindPackages("",
148 BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
149 | BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
150 | BSolver::B_FIND_IN_PROVIDES,
151 packages);
152 if (result != B_OK) {
153 BString message(B_TRANSLATE("An error occurred while "
154 "obtaining the package list: %message%"));
155 message.ReplaceFirst("%message%", strerror(result));
156 _NotifyError(message.String());
157 return B_ERROR;
158 }
159
160 if (packages.IsEmpty())
161 return B_ERROR;
162
163 PackageInfoMap foundPackages;
164 // if a given package is installed locally, we will potentially
165 // get back multiple entries, one for each local installation
166 // location, and one for each remote repository the package
167 // is available in. The above map is used to ensure that in such
168 // cases we consolidate the information, rather than displaying
169 // duplicates
170 PackageInfoMap remotePackages;
171 // any package that we find in a remote repository goes in this map.
172 // this is later used to discern which packages came from a local
173 // installation only, as those must be handled a bit differently
174 // upon uninstallation, since we'd no longer be able to pull them
175 // down remotely.
176 BStringList systemFlaggedPackages;
177 // any packages flagged as a system package are added to this list.
178 // such packages cannot be uninstalled, nor can any of their deps.
179 PackageInfoMap systemInstalledPackages;
180 // any packages installed in system are added to this list.
181 // This is later used for dependency resolution of the actual
182 // system packages in order to compute the list of protected
183 // dependencies indicated above.
184
185 for (int32 i = 0; i < packages.CountItems(); i++) {
186 BSolverPackage* package = packages.ItemAt(i);
187 const BPackageInfo& repoPackageInfo = package->Info();
188 const BString repositoryName = package->Repository()->Name();
189 PackageInfoRef modelInfo;
190 PackageInfoMap::iterator it = foundPackages.find(
191 repoPackageInfo.Name());
192 if (it != foundPackages.end())
193 modelInfo.SetTo(it->second);
194 else {
195 // Add new package info
196 modelInfo.SetTo(new(std::nothrow) PackageInfo(repoPackageInfo),
197 true);
198
199 if (!modelInfo.IsSet())
200 return B_ERROR;
201
202 foundPackages[repoPackageInfo.Name()] = modelInfo;
203 }
204
205 PackageLocalInfoRef localInfo = PackageUtils::NewLocalInfo(modelInfo);
206
207 // The package list here considers those packages that are installed
208 // in the system as well as those that exist in remote repositories.
209 // It is better if the 'depot name' is from the remote repository
210 // because then it will be possible to perform a rating on it later.
211
212 if (modelInfo->DepotName().IsEmpty()
213 || modelInfo->DepotName() == REPOSITORY_NAME_SYSTEM
214 || modelInfo->DepotName() == REPOSITORY_NAME_INSTALLED) {
215 modelInfo->SetDepotName(repositoryName);
216 }
217
218 modelInfo->AddListener(fPackageInfoListener);
219
220 BSolverRepository* repository = package->Repository();
221 BPackageManager::RemoteRepository* remoteRepository =
222 dynamic_cast<BPackageManager::RemoteRepository*>(repository);
223
224 if (remoteRepository != NULL) {
225
226 std::vector<DepotInfoRef>::iterator it;
227
228 for (it = depots.begin(); it != depots.end(); it++) {
229 if ((*it)->Identifier() == remoteRepository->Config().Identifier()) {
230 break;
231 }
232 }
233
234 if (it == depots.end()) {
235 HDDEBUG("pkg [%s] repository [%s] not recognized --> ignored",
236 modelInfo->Name().String(), repositoryName.String());
237 } else {
238 DepotInfoRef depot = *it;
239 depot->AddPackage(modelInfo);
240 HDTRACE("pkg [%s] assigned to [%s]",
241 modelInfo->Name().String(), repositoryName.String());
242 }
243
244 remotePackages[modelInfo->Name()] = modelInfo;
245 } else {
246 if (repository == static_cast<const BSolverRepository*>(manager.SystemRepository())) {
247 localInfo->AddInstallationLocation(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
248 if (!localInfo->IsSystemPackage())
249 systemInstalledPackages[repoPackageInfo.FileName()] = modelInfo;
250 } else if (repository
251 == static_cast<const BSolverRepository*>(manager.HomeRepository())) {
252 localInfo->AddInstallationLocation(B_PACKAGE_INSTALLATION_LOCATION_HOME);
253 }
254
255 }
256
257 if (localInfo->IsSystemPackage())
258 systemFlaggedPackages.Add(repoPackageInfo.FileName());
259
260 modelInfo->SetLocalInfo(localInfo);
261 }
262
263 BAutolock lock(fModel->Lock());
264
265 if (fForce)
266 fModel->Clear();
267
268 // filter remote packages from the found list
269 // any packages remaining will be locally installed packages
270 // that weren't acquired from a repository
271 for (PackageInfoMap::iterator it = remotePackages.begin();
272 it != remotePackages.end(); it++) {
273 foundPackages.erase(it->first);
274 }
275
276 if (!foundPackages.empty()) {
277 BString repoName = B_TRANSLATE("Local");
278 DepotInfoRef depotInfoRef(new(std::nothrow) DepotInfo(repoName), true);
279
280 if (!depotInfoRef.IsSet())
281 HDFATAL("unable to create a new depot info - memory exhaustion");
282
283 depots.push_back(depotInfoRef);
284
285 for (PackageInfoMap::iterator it = foundPackages.begin();
286 it != foundPackages.end(); ++it) {
287 depotInfoRef->AddPackage(it->second);
288 }
289 }
290
291 {
292 std::vector<DepotInfoRef>::iterator it;
293 for (it = depots.begin(); it != depots.end(); it++)
294 fModel->MergeOrAddDepot(*it);
295 }
296
297 // compute the OS package dependencies
298 try {
299 // create the solver
300 BSolver* solver;
301 status_t error = BSolver::Create(solver);
302 if (error != B_OK)
303 throw BFatalErrorException(error, "Failed to create solver.");
304
305 ObjectDeleter<BSolver> solverDeleter(solver);
306 BPath systemPath;
307 error = find_directory(B_SYSTEM_PACKAGES_DIRECTORY, &systemPath);
308 if (error != B_OK) {
309 throw BFatalErrorException(error,
310 "Unable to retrieve system packages directory.");
311 }
312
313 // add the "installed" repository with the given packages
314 BSolverRepository installedRepository;
315 {
316 BRepositoryBuilder installedRepositoryBuilder(installedRepository,
317 REPOSITORY_NAME_INSTALLED);
318 for (int32 i = 0; i < systemFlaggedPackages.CountStrings(); i++) {
319 BPath packagePath(systemPath);
320 packagePath.Append(systemFlaggedPackages.StringAt(i));
321 installedRepositoryBuilder.AddPackage(packagePath.Path());
322 }
323 installedRepositoryBuilder.AddToSolver(solver, true);
324 }
325
326 // add system repository
327 BSolverRepository systemRepository;
328 {
329 BRepositoryBuilder systemRepositoryBuilder(systemRepository,
330 REPOSITORY_NAME_SYSTEM);
331 for (PackageInfoMap::iterator it = systemInstalledPackages.begin();
332 it != systemInstalledPackages.end(); it++) {
333 BPath packagePath(systemPath);
334 packagePath.Append(it->first);
335 systemRepositoryBuilder.AddPackage(packagePath.Path());
336 }
337 systemRepositoryBuilder.AddToSolver(solver, false);
338 }
339
340 // solve
341 error = solver->VerifyInstallation();
342 if (error != B_OK) {
343 throw BFatalErrorException(error, "Failed to compute packages to "
344 "install.");
345 }
346
347 BSolverResult solverResult;
348 error = solver->GetResult(solverResult);
349 if (error != B_OK) {
350 throw BFatalErrorException(error, "Failed to retrieve system "
351 "package dependency list.");
352 }
353
354 for (int32 i = 0; const BSolverResultElement* element
355 = solverResult.ElementAt(i); i++) {
356 BSolverPackage* package = element->Package();
357 if (element->Type() == BSolverResultElement::B_TYPE_INSTALL) {
358 PackageInfoMap::iterator it = systemInstalledPackages.find(
359 package->Info().FileName());
360 if (it != systemInstalledPackages.end()) {
361 PackageInfoRef systemInstalledPackage = it->second;
362 PackageLocalInfoRef localInfo
363 = PackageUtils::NewLocalInfo(systemInstalledPackage);
364 localInfo->SetSystemDependency(true);
365 systemInstalledPackage->SetLocalInfo(localInfo);
366 }
367 }
368 }
369 } catch (BFatalErrorException& ex) {
370 HDERROR("Fatal exception occurred while resolving system dependencies: "
371 "%s, details: %s", strerror(ex.Error()), ex.Details().String());
372 } catch (BNothingToDoException&) {
373 // do nothing
374 } catch (BException& ex) {
375 HDERROR("Exception occurred while resolving system dependencies: %s",
376 ex.Message().String());
377 } catch (...) {
378 HDERROR("Unknown exception occurred while resolving system "
379 "dependencies.");
380 }
381
382 HDDEBUG("did refresh the package list");
383
384 return B_OK;
385 }
386
387
388 void
_NotifyError(const BString & messageText) const389 LocalPkgDataLoadProcess::_NotifyError(const BString& messageText) const
390 {
391 HDERROR("an error has arisen loading data of packages from local : %s",
392 messageText.String());
393 AppUtils::NotifySimpleError(
394 B_TRANSLATE("Local repository load error"),
395 messageText);
396 }
397