1/* 2 * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "PackageManager.h" 8 9#include <Catalog.h> 10#include <Notification.h> 11#include <package/DownloadFileRequest.h> 12#include <package/RefreshRepositoryRequest.h> 13#include <package/solver/SolverPackage.h> 14#include <package/solver/SolverPackageSpecifierList.h> 15#include <package/solver/SolverProblem.h> 16#include <package/solver/SolverProblemSolution.h> 17 18#include <AutoDeleter.h> 19#include <package/manager/Exceptions.h> 20#include <package/manager/RepositoryBuilder.h> 21#include <Server.h> 22 23#include "ProblemWindow.h" 24#include "ResultWindow.h" 25#include "Root.h" 26#include "Volume.h" 27 28#undef B_TRANSLATION_CONTEXT 29#define B_TRANSLATION_CONTEXT "PackageManager" 30 31using BPackageKit::BManager::BPrivate::BAbortedByUserException; 32using BPackageKit::BManager::BPrivate::BFatalErrorException; 33using BPackageKit::BManager::BPrivate::BRepositoryBuilder; 34 35 36PackageManager::PackageManager(Root* root, Volume* volume) 37 : 38 BPackageManager(volume->Location(), this, this), 39 BPackageManager::UserInteractionHandler(), 40 fRoot(root), 41 fVolume(volume), 42 fSolverPackages(), 43 fPackagesAddedByUser(), 44 fPackagesRemovedByUser(), 45 fProblemWindow(NULL) 46{ 47} 48 49 50PackageManager::~PackageManager() 51{ 52 if (fProblemWindow != NULL) 53 fProblemWindow->PostMessage(B_QUIT_REQUESTED); 54} 55 56 57void 58PackageManager::HandleUserChanges() 59{ 60 const PackageSet& packagesToActivate = fVolume->PackagesToBeActivated(); 61 const PackageSet& packagesToDeactivate = fVolume->PackagesToBeDeactivated(); 62 63 if (packagesToActivate.empty() && packagesToDeactivate.empty()) 64 return; 65 66 if (packagesToActivate.empty()) { 67 // only packages removed -- use uninstall mode 68 Init(B_ADD_INSTALLED_REPOSITORIES); 69 70 BSolverPackageSpecifierList packagesToUninstall; 71 for (PackageSet::const_iterator it = packagesToDeactivate.begin(); 72 it != packagesToDeactivate.end(); ++it) { 73 BSolverPackage* solverPackage = _SolverPackageFor(*it); 74 if (solverPackage == NULL) 75 continue; 76 77 if (!packagesToUninstall.AppendSpecifier(solverPackage)) 78 throw std::bad_alloc(); 79 fPackagesRemovedByUser.insert(solverPackage); 80 } 81 82 if (fPackagesRemovedByUser.empty()) 83 return; 84 85 Uninstall(packagesToUninstall); 86 } else { 87 // packages added and (possibly) remove -- manually add/remove those 88 // from the repository and use verify mode 89 Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES 90 | B_REFRESH_REPOSITORIES); 91 92 // disable and remove uninstalled packages 93 InstalledRepository& repository = InstallationRepository(); 94 for (PackageSet::const_iterator it = packagesToDeactivate.begin(); 95 it != packagesToDeactivate.end(); ++it) { 96 BSolverPackage* solverPackage = _SolverPackageFor(*it); 97 if (solverPackage == NULL) 98 continue; 99 100 repository.DisablePackage(solverPackage); 101 if (!repository.PackagesToDeactivate().AddItem(solverPackage)) 102 throw std::bad_alloc(); 103 fPackagesRemovedByUser.insert(solverPackage); 104 } 105 106 // add new packages 107 BRepositoryBuilder repositoryBuilder(repository); 108 for (PackageSet::const_iterator it = packagesToActivate.begin(); 109 it != packagesToActivate.end(); ++it) { 110 Package* package = *it; 111 BSolverPackage* solverPackage; 112 repositoryBuilder.AddPackage(package->Info(), NULL, &solverPackage); 113 fSolverPackages[package] = solverPackage; 114 if (!repository.PackagesToActivate().AddItem(solverPackage)) 115 throw std::bad_alloc(); 116 fPackagesAddedByUser.insert(solverPackage); 117 } 118 119 if (fPackagesRemovedByUser.empty() && fPackagesAddedByUser.empty()) 120 return; 121 122// TODO: When packages are moved out of the packages directory, we can't create 123// a complete "old-state" directory. 124 125 VerifyInstallation(); 126 } 127} 128 129 130void 131PackageManager::InitInstalledRepository(InstalledRepository& repository) 132{ 133 const char* name = repository.InitialName(); 134 BRepositoryBuilder repositoryBuilder(repository, name); 135 136 if (Volume* volume = fRoot->GetVolume(repository.Location())) { 137 for (PackageFileNameHashTable::Iterator it 138 = volume->PackagesByFileNameIterator(); it.HasNext();) { 139 Package* package = it.Next(); 140 if (package->IsActive()) { 141 BSolverPackage* solverPackage; 142 repositoryBuilder.AddPackage(package->Info(), NULL, 143 &solverPackage); 144 fSolverPackages[package] = solverPackage; 145 } 146 } 147 } 148} 149 150 151void 152PackageManager::ResultComputed(InstalledRepository& repository) 153{ 154 // Normalize the result, i.e. remove the packages added by the user which 155 // have been removed again in the problem resolution process, and vice 156 // versa. 157 if (repository.Location() != fVolume->Location()) 158 return; 159 160 PackageList& packagesToActivate = repository.PackagesToActivate(); 161 PackageList& packagesToDeactivate = repository.PackagesToDeactivate(); 162 163 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); 164 i++) { 165 if (fPackagesAddedByUser.erase(package) == 0) 166 continue; 167 168 for (SolverPackageMap::iterator it = fSolverPackages.begin(); 169 it != fSolverPackages.end(); ++it) { 170 if (it->second == package) { 171 fSolverPackages.erase(it); 172 break; 173 } 174 } 175 176 repository.EnablePackage(package); 177 packagesToDeactivate.RemoveItemAt(i--); 178 packagesToActivate.RemoveItem(package); 179 repository.DeletePackage(package); 180 } 181 182 for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); 183 i++) { 184 if (fPackagesRemovedByUser.erase(package) == 0) 185 continue; 186 187 repository.EnablePackage(package); 188 packagesToActivate.RemoveItemAt(i--); 189 packagesToDeactivate.RemoveItem(package); 190 191 // Note: We keep the package activated, but nonetheless it is gone, 192 // since the user has removed it from the packages directory. So unless 193 // the user moves it back, we won't find it upon next reboot. 194 // TODO: We probable even run into trouble when the user moves in a 195 // replacement afterward. 196 } 197} 198 199 200status_t 201PackageManager::PrepareTransaction(Transaction& transaction) 202{ 203 Volume* volume = fRoot->GetVolume(transaction.Repository().Location()); 204 if (volume == NULL) 205 return B_BAD_VALUE; 206 207 return volume->CreateTransaction(transaction.Repository().Location(), 208 transaction.ActivationTransaction(), 209 transaction.TransactionDirectory()); 210} 211 212 213status_t 214PackageManager::CommitTransaction(Transaction& transaction, 215 BCommitTransactionResult& _result) 216{ 217 Volume* volume = fRoot->GetVolume(transaction.Repository().Location()); 218 if (volume == NULL) 219 return B_BAD_VALUE; 220 221 // Get the packages that have already been added to/removed from the 222 // packages directory and thus need to be handled specially by 223 // Volume::CommitTransaction(). 224 PackageSet packagesAlreadyAdded; 225 PackageSet packagesAlreadyRemoved; 226 if (volume == fVolume) { 227 const PackageSet& packagesToActivate = volume->PackagesToBeActivated(); 228 for (PackageSet::const_iterator it = packagesToActivate.begin(); 229 it != packagesToActivate.end(); ++it) { 230 Package* package = *it; 231 if (fPackagesAddedByUser.find(_SolverPackageFor(package)) 232 != fPackagesAddedByUser.end()) { 233 packagesAlreadyAdded.insert(package); 234 } 235 } 236 237 const PackageSet& packagesToDeactivate 238 = volume->PackagesToBeDeactivated(); 239 for (PackageSet::const_iterator it = packagesToDeactivate.begin(); 240 it != packagesToDeactivate.end(); ++it) { 241 Package* package = *it; 242 if (fPackagesRemovedByUser.find(_SolverPackageFor(package)) 243 != fPackagesRemovedByUser.end()) { 244 packagesAlreadyRemoved.insert(package); 245 } 246 } 247 } 248 249 volume->CommitTransaction(transaction.ActivationTransaction(), 250 packagesAlreadyAdded, packagesAlreadyRemoved, _result); 251 return B_OK; 252} 253 254 255void 256PackageManager::HandleProblems() 257{ 258 _InitGui(); 259 260 if (fProblemWindow == NULL) 261 fProblemWindow = new ProblemWindow; 262 263 if (!fProblemWindow->Go(fSolver, fPackagesAddedByUser, 264 fPackagesRemovedByUser)) { 265 throw BAbortedByUserException(); 266 } 267} 268 269 270void 271PackageManager::ConfirmChanges(bool fromMostSpecific) 272{ 273 // Check whether there are any changes other than those made by the user. 274 _InitGui(); 275 ResultWindow* window = new ResultWindow; 276 ObjectDeleter<ResultWindow> windowDeleter(window); 277 278 bool hasOtherChanges = false; 279 int32 count = fInstalledRepositories.CountItems(); 280 if (fromMostSpecific) { 281 for (int32 i = count - 1; i >= 0; i--) 282 hasOtherChanges 283 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 284 } else { 285 for (int32 i = 0; i < count; i++) 286 hasOtherChanges 287 |= _AddResults(*fInstalledRepositories.ItemAt(i), window); 288 } 289 290 if (!hasOtherChanges) 291 return; 292 293 // show the window 294 if (windowDeleter.Detach()->Go() == 0) 295 throw BAbortedByUserException(); 296} 297 298 299void 300PackageManager::Warn(status_t error, const char* format, ...) 301{ 302 va_list args; 303 va_start(args, format); 304 BString message; 305 message.SetToFormatVarArgs(format, args); 306 va_end(args); 307 308 if (error != B_OK) 309 message << BString().SetToFormat(": %s", strerror(error)); 310 311 BNotification notification(B_ERROR_NOTIFICATION); 312 notification.SetGroup(B_TRANSLATE("Package daemon")); 313 notification.SetTitle(B_TRANSLATE("Warning")); 314 notification.SetContent(message); 315 notification.Send(); 316} 317 318 319void 320PackageManager::ProgressPackageDownloadStarted(const char* packageName) 321{ 322} 323 324 325void 326PackageManager::ProgressPackageDownloadActive(const char* packageName, 327 float completionPercentage, off_t bytes, off_t totalBytes) 328{ 329} 330 331 332void 333PackageManager::ProgressPackageDownloadComplete(const char* packageName) 334{ 335} 336 337 338void 339PackageManager::ProgressPackageChecksumStarted(const char* title) 340{ 341} 342 343 344void 345PackageManager::ProgressPackageChecksumComplete(const char* title) 346{ 347} 348 349 350void 351PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) 352{ 353} 354 355 356void 357PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, 358 const BCommitTransactionResult& result) 359{ 360} 361 362 363void 364PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) 365{ 366} 367 368 369void 370PackageManager::JobFailed(BSupportKit::BJob* job) 371{ 372// TODO:... 373} 374 375 376void 377PackageManager::JobAborted(BSupportKit::BJob* job) 378{ 379// TODO:... 380} 381 382 383bool 384PackageManager::_AddResults(InstalledRepository& repository, 385 ResultWindow* window) 386{ 387 if (!repository.HasChanges()) 388 return false; 389 390 return window->AddLocationChanges(repository.Name(), 391 repository.PackagesToActivate(), fPackagesAddedByUser, 392 repository.PackagesToDeactivate(), fPackagesRemovedByUser); 393} 394 395 396BSolverPackage* 397PackageManager::_SolverPackageFor(Package* package) const 398{ 399 SolverPackageMap::const_iterator it = fSolverPackages.find(package); 400 return it != fSolverPackages.end() ? it->second : NULL; 401} 402 403 404void 405PackageManager::_InitGui() 406{ 407 BServer* server = dynamic_cast<BServer*>(be_app); 408 if (server == NULL || server->InitGUIContext() != B_OK) 409 throw BFatalErrorException("failed to initialize the GUI"); 410} 411