/* * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include "PackageManager.h" #include #include #include #include #include #include #include #include #include #include #include #include "ProblemWindow.h" #include "ResultWindow.h" #include "Root.h" #include "Volume.h" using BPackageKit::BManager::BPrivate::BAbortedByUserException; using BPackageKit::BManager::BPrivate::BFatalErrorException; using BPackageKit::BManager::BPrivate::BRepositoryBuilder; PackageManager::PackageManager(Root* root, Volume* volume) : BPackageManager(volume->Location(), this, this), BPackageManager::UserInteractionHandler(), fRoot(root), fVolume(volume), fSolverPackages(), fPackagesAddedByUser(), fPackagesRemovedByUser(), fProblemWindow(NULL) { } PackageManager::~PackageManager() { if (fProblemWindow != NULL) fProblemWindow->PostMessage(B_QUIT_REQUESTED); } void PackageManager::HandleUserChanges() { const PackageSet& packagesToActivate = fVolume->PackagesToBeActivated(); const PackageSet& packagesToDeactivate = fVolume->PackagesToBeDeactivated(); if (packagesToActivate.empty() && packagesToDeactivate.empty()) return; if (packagesToActivate.empty()) { // only packages removed -- use uninstall mode Init(B_ADD_INSTALLED_REPOSITORIES); BSolverPackageSpecifierList packagesToUninstall; for (PackageSet::const_iterator it = packagesToDeactivate.begin(); it != packagesToDeactivate.end(); ++it) { BSolverPackage* solverPackage = _SolverPackageFor(*it); if (solverPackage == NULL) continue; if (!packagesToUninstall.AppendSpecifier(solverPackage)) throw std::bad_alloc(); fPackagesRemovedByUser.insert(solverPackage); } if (fPackagesRemovedByUser.empty()) return; Uninstall(packagesToUninstall); } else { // packages added and (possibly) remove -- manually add/remove those // from the repository and use verify mode Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES | B_REFRESH_REPOSITORIES); // disable and remove uninstalled packages InstalledRepository& repository = InstallationRepository(); for (PackageSet::const_iterator it = packagesToDeactivate.begin(); it != packagesToDeactivate.end(); ++it) { BSolverPackage* solverPackage = _SolverPackageFor(*it); if (solverPackage == NULL) continue; repository.DisablePackage(solverPackage); if (!repository.PackagesToDeactivate().AddItem(solverPackage)) throw std::bad_alloc(); fPackagesRemovedByUser.insert(solverPackage); } // add new packages BRepositoryBuilder repositoryBuilder(repository); for (PackageSet::const_iterator it = packagesToActivate.begin(); it != packagesToActivate.end(); ++it) { Package* package = *it; BSolverPackage* solverPackage; repositoryBuilder.AddPackage(package->Info(), NULL, &solverPackage); fSolverPackages[package] = solverPackage; if (!repository.PackagesToActivate().AddItem(solverPackage)) throw std::bad_alloc(); fPackagesAddedByUser.insert(solverPackage); } if (fPackagesRemovedByUser.empty() && fPackagesAddedByUser.empty()) return; // TODO: When packages are moved out of the packages directory, we can't create // a complete "old-state" directory. VerifyInstallation(); } } void PackageManager::InitInstalledRepository(InstalledRepository& repository) { const char* name = repository.InitialName(); BRepositoryBuilder repositoryBuilder(repository, name); if (Volume* volume = fRoot->GetVolume(repository.Location())) { for (PackageFileNameHashTable::Iterator it = volume->PackagesByFileNameIterator(); it.HasNext();) { Package* package = it.Next(); if (package->IsActive()) { BSolverPackage* solverPackage; repositoryBuilder.AddPackage(package->Info(), NULL, &solverPackage); fSolverPackages[package] = solverPackage; } } } } void PackageManager::ResultComputed(InstalledRepository& repository) { // Normalize the result, i.e. remove the packages added by the user which // have been removed again in the problem resolution process, and vice // versa. if (repository.Location() != fVolume->Location()) return; PackageList& packagesToActivate = repository.PackagesToActivate(); PackageList& packagesToDeactivate = repository.PackagesToDeactivate(); for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); i++) { if (fPackagesAddedByUser.erase(package) == 0) continue; for (SolverPackageMap::iterator it = fSolverPackages.begin(); it != fSolverPackages.end(); ++it) { if (it->second == package) { fSolverPackages.erase(it); break; } } repository.EnablePackage(package); packagesToDeactivate.RemoveItemAt(i--); packagesToActivate.RemoveItem(package); repository.DeletePackage(package); } for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); i++) { if (fPackagesRemovedByUser.erase(package) == 0) continue; repository.EnablePackage(package); packagesToActivate.RemoveItemAt(i--); packagesToDeactivate.RemoveItem(package); // Note: We keep the package activated, but nonetheless it is gone, // since the user has removed it from the packages directory. So unless // the user moves it back, we won't find it upon next reboot. // TODO: We probable even run into trouble when the user moves in a // replacement afterward. } } status_t PackageManager::PrepareTransaction(Transaction& transaction) { Volume* volume = fRoot->GetVolume(transaction.Repository().Location()); if (volume == NULL) return B_BAD_VALUE; return volume->CreateTransaction(transaction.Repository().Location(), transaction.ActivationTransaction(), transaction.TransactionDirectory()); } status_t PackageManager::CommitTransaction(Transaction& transaction, BCommitTransactionResult& _result) { Volume* volume = fRoot->GetVolume(transaction.Repository().Location()); if (volume == NULL) return B_BAD_VALUE; // Get the packages that have already been added to/removed from the // packages directory and thus need to be handled specially by // Volume::CommitTransaction(). PackageSet packagesAlreadyAdded; PackageSet packagesAlreadyRemoved; if (volume == fVolume) { const PackageSet& packagesToActivate = volume->PackagesToBeActivated(); for (PackageSet::const_iterator it = packagesToActivate.begin(); it != packagesToActivate.end(); ++it) { Package* package = *it; if (fPackagesAddedByUser.find(_SolverPackageFor(package)) != fPackagesAddedByUser.end()) { packagesAlreadyAdded.insert(package); } } const PackageSet& packagesToDeactivate = volume->PackagesToBeDeactivated(); for (PackageSet::const_iterator it = packagesToDeactivate.begin(); it != packagesToDeactivate.end(); ++it) { Package* package = *it; if (fPackagesRemovedByUser.find(_SolverPackageFor(package)) != fPackagesRemovedByUser.end()) { packagesAlreadyRemoved.insert(package); } } } volume->CommitTransaction(transaction.ActivationTransaction(), packagesAlreadyAdded, packagesAlreadyRemoved, _result); return B_OK; } void PackageManager::HandleProblems() { _InitGui(); if (fProblemWindow == NULL) fProblemWindow = new ProblemWindow; if (!fProblemWindow->Go(fSolver, fPackagesAddedByUser, fPackagesRemovedByUser)) { throw BAbortedByUserException(); } } void PackageManager::ConfirmChanges(bool fromMostSpecific) { // Check whether there are any changes other than those made by the user. _InitGui(); ResultWindow* window = new ResultWindow; ObjectDeleter windowDeleter(window); bool hasOtherChanges = false; int32 count = fInstalledRepositories.CountItems(); if (fromMostSpecific) { for (int32 i = count - 1; i >= 0; i--) hasOtherChanges |= _AddResults(*fInstalledRepositories.ItemAt(i), window); } else { for (int32 i = 0; i < count; i++) hasOtherChanges |= _AddResults(*fInstalledRepositories.ItemAt(i), window); } if (!hasOtherChanges) return; // show the window if (windowDeleter.Detach()->Go() == 0) throw BAbortedByUserException(); } void PackageManager::Warn(status_t error, const char* format, ...) { va_list args; va_start(args, format); BString message; message.SetToFormatVarArgs(format, args); va_end(args); if (error != B_OK) message << BString().SetToFormat(": %s", strerror(error)); BNotification notification(B_ERROR_NOTIFICATION); notification.SetTitle("Package Daemon"); notification.SetContent(message); notification.Send(); } void PackageManager::ProgressPackageDownloadStarted(const char* packageName) { } void PackageManager::ProgressPackageDownloadActive(const char* packageName, float completionPercentage) { } void PackageManager::ProgressPackageDownloadComplete(const char* packageName) { } void PackageManager::ProgressPackageChecksumStarted(const char* title) { } void PackageManager::ProgressPackageChecksumComplete(const char* title) { } void PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) { } void PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, const BCommitTransactionResult& result) { } void PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) { } void PackageManager::JobFailed(BJob* job) { // TODO:... } void PackageManager::JobAborted(BJob* job) { // TODO:... } bool PackageManager::_AddResults(InstalledRepository& repository, ResultWindow* window) { if (!repository.HasChanges()) return false; return window->AddLocationChanges(repository.Name(), repository.PackagesToActivate(), fPackagesAddedByUser, repository.PackagesToDeactivate(), fPackagesRemovedByUser); } BSolverPackage* PackageManager::_SolverPackageFor(Package* package) const { SolverPackageMap::const_iterator it = fSolverPackages.find(package); return it != fSolverPackages.end() ? it->second : NULL; } void PackageManager::_InitGui() { BServer* server = dynamic_cast(be_app); if (server == NULL || server->InitGUIContext() != B_OK) throw BFatalErrorException("failed to initialize the GUI"); }