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