1 /* 2 * Copyright 2018-2020, 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 "RepositoryUrlUtils.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 58 LocalPkgDataLoadProcess::LocalPkgDataLoadProcess( 59 PackageInfoListener* packageInfoListener, 60 Model *model, bool force) 61 : 62 AbstractProcess(), 63 fModel(model), 64 fForce(force), 65 fPackageInfoListener(packageInfoListener) 66 { 67 } 68 69 70 LocalPkgDataLoadProcess::~LocalPkgDataLoadProcess() 71 { 72 } 73 74 75 const char* 76 LocalPkgDataLoadProcess::Name() const 77 { 78 return "LocalPkgDataLoadProcess"; 79 } 80 81 82 const char* 83 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 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->SetURL(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 // The package list here considers those packages that are installed 206 // in the system as well as those that exist in remote repositories. 207 // It is better if the 'depot name' is from the remote repository 208 // because then it will be possible to perform a rating on it later. 209 210 if (modelInfo->DepotName().IsEmpty() 211 || modelInfo->DepotName() == REPOSITORY_NAME_SYSTEM 212 || modelInfo->DepotName() == REPOSITORY_NAME_INSTALLED) { 213 modelInfo->SetDepotName(repositoryName); 214 } 215 216 modelInfo->AddListener(fPackageInfoListener); 217 218 BSolverRepository* repository = package->Repository(); 219 BPackageManager::RemoteRepository* remoteRepository = 220 dynamic_cast<BPackageManager::RemoteRepository*>(repository); 221 222 if (remoteRepository != NULL) { 223 224 std::vector<DepotInfoRef>::iterator it; 225 226 for (it = depots.begin(); it != depots.end(); it++) { 227 if (RepositoryUrlUtils::EqualsNormalized( 228 (*it)->URL(), remoteRepository->Config().Identifier())) { 229 break; 230 } 231 } 232 233 if (it == depots.end()) { 234 HDDEBUG("pkg [%s] repository [%s] not recognized --> ignored", 235 modelInfo->Name().String(), repositoryName.String()); 236 } else { 237 (*it)->AddPackage(modelInfo); 238 HDTRACE("pkg [%s] assigned to [%s]", 239 modelInfo->Name().String(), repositoryName.String()); 240 } 241 242 remotePackages[modelInfo->Name()] = modelInfo; 243 } else { 244 if (repository == static_cast<const BSolverRepository*>( 245 manager.SystemRepository())) { 246 modelInfo->AddInstallationLocation( 247 B_PACKAGE_INSTALLATION_LOCATION_SYSTEM); 248 if (!modelInfo->IsSystemPackage()) { 249 systemInstalledPackages[repoPackageInfo.FileName()] 250 = modelInfo; 251 } 252 } else if (repository == static_cast<const BSolverRepository*>( 253 manager.HomeRepository())) { 254 modelInfo->AddInstallationLocation( 255 B_PACKAGE_INSTALLATION_LOCATION_HOME); 256 } 257 } 258 259 if (modelInfo->IsSystemPackage()) 260 systemFlaggedPackages.Add(repoPackageInfo.FileName()); 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 it->second->SetSystemDependency(true); 362 } 363 } 364 } catch (BFatalErrorException& ex) { 365 HDERROR("Fatal exception occurred while resolving system dependencies: " 366 "%s, details: %s", strerror(ex.Error()), ex.Details().String()); 367 } catch (BNothingToDoException&) { 368 // do nothing 369 } catch (BException& ex) { 370 HDERROR("Exception occurred while resolving system dependencies: %s", 371 ex.Message().String()); 372 } catch (...) { 373 HDERROR("Unknown exception occurred while resolving system " 374 "dependencies."); 375 } 376 377 HDDEBUG("did refresh the package list"); 378 379 return B_OK; 380 } 381 382 383 void 384 LocalPkgDataLoadProcess::_NotifyError(const BString& messageText) const 385 { 386 HDERROR("an error has arisen loading data of packages from local : %s", 387 messageText.String()); 388 AppUtils::NotifySimpleError( 389 B_TRANSLATE("Local repository load error"), 390 messageText); 391 } 392