* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Rene Gollent, rene@gollent.com.
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* All rights reserved. Distributed under the terms of the MIT License.
*
* Note that this file included code earlier from `MainWindow.cpp` and
* copyrights have been latterly been carried across in 2021.
*/
#include "LocalPkgDataLoadProcess.h"
#include <map>
#include <vector>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Autolock.h>
#include <Catalog.h>
#include <Roster.h>
#include <StringList.h>
#include "AppUtils.h"
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "PackageInfo.h"
#include "PackageManager.h"
#include "PackageUtils.h"
#include "RepositoryUrlUtils.h"
#include <package/Context.h>
#include <package/manager/Exceptions.h>
#include <package/manager/RepositoryBuilder.h>
#include <package/PackageRoster.h>
#include "package/RepositoryCache.h"
#include <package/RefreshRepositoryRequest.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverResult.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LocalPkgDataLoadProcess"
using namespace BPackageKit;
using namespace BPackageKit::BManager::BPrivate;
typedef std::map<BString, PackageInfoRef> PackageInfoMap;
\param packageInfoListener is assigned to each package model object.
*/
LocalPkgDataLoadProcess::LocalPkgDataLoadProcess(
PackageInfoListenerRef packageInfoListener,
Model *model, bool force)
:
AbstractProcess(),
fModel(model),
fForce(force),
fPackageInfoListener(packageInfoListener)
{
}
LocalPkgDataLoadProcess::~LocalPkgDataLoadProcess()
{
}
const char*
LocalPkgDataLoadProcess::Name() const
{
return "LocalPkgDataLoadProcess";
}
const char*
LocalPkgDataLoadProcess::Description() const
{
return B_TRANSLATE("Reading repository data");
}
from MainWindow.cpp in order that the logic fits into the background
loading processes. The code needs to be broken up into methods with some
sort of a state object carrying the state of the process. As part of this,
better error handling and error reporting would also be advantageous.
*/
status_t
LocalPkgDataLoadProcess::RunInternal()
{
HDDEBUG("[%s] will refresh the package list", Name());
BPackageRoster roster;
BStringList repositoryNames;
status_t result = roster.GetRepositoryNames(repositoryNames);
if (result != B_OK)
return result;
std::vector<DepotInfoRef> depots(repositoryNames.CountStrings());
for (int32 i = 0; i < repositoryNames.CountStrings(); i++) {
const BString& repoName = repositoryNames.StringAt(i);
DepotInfoRef depotInfoRef = DepotInfoRef(
new(std::nothrow) DepotInfo(repoName), true);
if (!depotInfoRef.IsSet())
HDFATAL("unable to create new depot info - memory exhaustion");
BRepositoryConfig repoConfig;
status_t getRepositoryConfigStatus = roster.GetRepositoryConfig(
repoName, &repoConfig);
if (getRepositoryConfigStatus == B_OK) {
depotInfoRef->SetURL(repoConfig.Identifier());
HDDEBUG("[%s] local repository [%s] identifier; [%s]",
Name(), repoName.String(), repoConfig.Identifier().String());
} else {
HDINFO("[%s] unable to obtain the repository config for local "
"repository '%s'; %s", Name(),
repoName.String(), strerror(getRepositoryConfigStatus));
}
depots[i] = depotInfoRef;
}
PackageManager manager(B_PACKAGE_INSTALLATION_LOCATION_HOME);
try {
manager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES
| PackageManager::B_ADD_REMOTE_REPOSITORIES);
} catch (BException& ex) {
BString message(B_TRANSLATE("An error occurred while "
"initializing the package manager: %message%"));
message.ReplaceFirst("%message%", ex.Message());
_NotifyError(message.String());
return B_ERROR;
}
BObjectList<BSolverPackage> packages;
result = manager.Solver()->FindPackages("",
BSolver::B_FIND_CASE_INSENSITIVE | BSolver::B_FIND_IN_NAME
| BSolver::B_FIND_IN_SUMMARY | BSolver::B_FIND_IN_DESCRIPTION
| BSolver::B_FIND_IN_PROVIDES,
packages);
if (result != B_OK) {
BString message(B_TRANSLATE("An error occurred while "
"obtaining the package list: %message%"));
message.ReplaceFirst("%message%", strerror(result));
_NotifyError(message.String());
return B_ERROR;
}
if (packages.IsEmpty())
return B_ERROR;
PackageInfoMap foundPackages;
PackageInfoMap remotePackages;
BStringList systemFlaggedPackages;
PackageInfoMap systemInstalledPackages;
for (int32 i = 0; i < packages.CountItems(); i++) {
BSolverPackage* package = packages.ItemAt(i);
const BPackageInfo& repoPackageInfo = package->Info();
const BString repositoryName = package->Repository()->Name();
PackageInfoRef modelInfo;
PackageInfoMap::iterator it = foundPackages.find(
repoPackageInfo.Name());
if (it != foundPackages.end())
modelInfo.SetTo(it->second);
else {
modelInfo.SetTo(new(std::nothrow) PackageInfo(repoPackageInfo),
true);
if (!modelInfo.IsSet())
return B_ERROR;
modelInfo->SetSize(_DeriveSize(modelInfo));
foundPackages[repoPackageInfo.Name()] = modelInfo;
}
if (modelInfo->DepotName().IsEmpty()
|| modelInfo->DepotName() == REPOSITORY_NAME_SYSTEM
|| modelInfo->DepotName() == REPOSITORY_NAME_INSTALLED) {
modelInfo->SetDepotName(repositoryName);
}
modelInfo->AddListener(fPackageInfoListener);
BSolverRepository* repository = package->Repository();
BPackageManager::RemoteRepository* remoteRepository =
dynamic_cast<BPackageManager::RemoteRepository*>(repository);
if (remoteRepository != NULL) {
std::vector<DepotInfoRef>::iterator it;
for (it = depots.begin(); it != depots.end(); it++) {
if (RepositoryUrlUtils::EqualsNormalized(
(*it)->URL(), remoteRepository->Config().Identifier())) {
break;
}
}
if (it == depots.end()) {
HDDEBUG("pkg [%s] repository [%s] not recognized --> ignored",
modelInfo->Name().String(), repositoryName.String());
} else {
(*it)->AddPackage(modelInfo);
HDTRACE("pkg [%s] assigned to [%s]",
modelInfo->Name().String(), repositoryName.String());
}
remotePackages[modelInfo->Name()] = modelInfo;
} else {
if (repository == static_cast<const BSolverRepository*>(
manager.SystemRepository())) {
modelInfo->AddInstallationLocation(
B_PACKAGE_INSTALLATION_LOCATION_SYSTEM);
if (!modelInfo->IsSystemPackage()) {
systemInstalledPackages[repoPackageInfo.FileName()]
= modelInfo;
}
} else if (repository == static_cast<const BSolverRepository*>(
manager.HomeRepository())) {
modelInfo->AddInstallationLocation(
B_PACKAGE_INSTALLATION_LOCATION_HOME);
}
}
if (modelInfo->IsSystemPackage())
systemFlaggedPackages.Add(repoPackageInfo.FileName());
}
BAutolock lock(fModel->Lock());
if (fForce)
fModel->Clear();
for (PackageInfoMap::iterator it = remotePackages.begin();
it != remotePackages.end(); it++) {
foundPackages.erase(it->first);
}
if (!foundPackages.empty()) {
BString repoName = B_TRANSLATE("Local");
DepotInfoRef depotInfoRef(new(std::nothrow) DepotInfo(repoName), true);
if (!depotInfoRef.IsSet())
HDFATAL("unable to create a new depot info - memory exhaustion");
depots.push_back(depotInfoRef);
for (PackageInfoMap::iterator it = foundPackages.begin();
it != foundPackages.end(); ++it) {
depotInfoRef->AddPackage(it->second);
}
}
{
std::vector<DepotInfoRef>::iterator it;
for (it = depots.begin(); it != depots.end(); it++)
fModel->MergeOrAddDepot(*it);
}
try {
BSolver* solver;
status_t error = BSolver::Create(solver);
if (error != B_OK)
throw BFatalErrorException(error, "Failed to create solver.");
ObjectDeleter<BSolver> solverDeleter(solver);
BPath systemPath;
error = find_directory(B_SYSTEM_PACKAGES_DIRECTORY, &systemPath);
if (error != B_OK) {
throw BFatalErrorException(error,
"Unable to retrieve system packages directory.");
}
BSolverRepository installedRepository;
{
BRepositoryBuilder installedRepositoryBuilder(installedRepository,
REPOSITORY_NAME_INSTALLED);
for (int32 i = 0; i < systemFlaggedPackages.CountStrings(); i++) {
BPath packagePath(systemPath);
packagePath.Append(systemFlaggedPackages.StringAt(i));
installedRepositoryBuilder.AddPackage(packagePath.Path());
}
installedRepositoryBuilder.AddToSolver(solver, true);
}
BSolverRepository systemRepository;
{
BRepositoryBuilder systemRepositoryBuilder(systemRepository,
REPOSITORY_NAME_SYSTEM);
for (PackageInfoMap::iterator it = systemInstalledPackages.begin();
it != systemInstalledPackages.end(); it++) {
BPath packagePath(systemPath);
packagePath.Append(it->first);
systemRepositoryBuilder.AddPackage(packagePath.Path());
}
systemRepositoryBuilder.AddToSolver(solver, false);
}
error = solver->VerifyInstallation();
if (error != B_OK) {
throw BFatalErrorException(error, "Failed to compute packages to "
"install.");
}
BSolverResult solverResult;
error = solver->GetResult(solverResult);
if (error != B_OK) {
throw BFatalErrorException(error, "Failed to retrieve system "
"package dependency list.");
}
for (int32 i = 0; const BSolverResultElement* element
= solverResult.ElementAt(i); i++) {
BSolverPackage* package = element->Package();
if (element->Type() == BSolverResultElement::B_TYPE_INSTALL) {
PackageInfoMap::iterator it = systemInstalledPackages.find(
package->Info().FileName());
if (it != systemInstalledPackages.end())
it->second->SetSystemDependency(true);
}
}
} catch (BFatalErrorException& ex) {
HDERROR("Fatal exception occurred while resolving system dependencies: "
"%s, details: %s", strerror(ex.Error()), ex.Details().String());
} catch (BNothingToDoException&) {
} catch (BException& ex) {
HDERROR("Exception occurred while resolving system dependencies: %s",
ex.Message().String());
} catch (...) {
HDERROR("Unknown exception occurred while resolving system "
"dependencies.");
}
HDDEBUG("did refresh the package list");
return B_OK;
}
off_t
LocalPkgDataLoadProcess::_DeriveSize(const PackageInfoRef package) const
{
BPath path;
if (PackageUtils::DeriveLocalFilePath(package.Get(), path) == B_OK) {
BEntry entry(path.Path());
if (entry.Exists()) {
off_t size;
if (entry.GetSize(&size) == B_OK)
return size;
else {
HDDEBUG("unable to get the size of local file [%s]",
path.Path());
}
}
else
HDDEBUG("the local file [%s] does not exist", path.Path());
}
else {
HDDEBUG("unable to get the local file of package [%s]",
package->Name().String());
}
return 0;
}
void
LocalPkgDataLoadProcess::_NotifyError(const BString& messageText) const
{
HDERROR("an error has arisen loading data of packages from local : %s",
messageText.String());
AppUtils::NotifySimpleError(
B_TRANSLATE("Local repository load error"),
messageText);
}