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 31 using BPackageKit::BManager::BPrivate::BAbortedByUserException; 32 using BPackageKit::BManager::BPrivate::BFatalErrorException; 33 using BPackageKit::BManager::BPrivate::BRepositoryBuilder; 34 35 36 PackageManager::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 50 PackageManager::~PackageManager() 51 { 52 if (fProblemWindow != NULL) 53 fProblemWindow->PostMessage(B_QUIT_REQUESTED); 54 } 55 56 57 void 58 PackageManager::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 130 void 131 PackageManager::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 151 void 152 PackageManager::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 200 status_t 201 PackageManager::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 213 status_t 214 PackageManager::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 255 void 256 PackageManager::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 270 void 271 PackageManager::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 299 void 300 PackageManager::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 319 void 320 PackageManager::ProgressPackageDownloadStarted(const char* packageName) 321 { 322 } 323 324 325 void 326 PackageManager::ProgressPackageDownloadActive(const char* packageName, 327 float completionPercentage, off_t bytes, off_t totalBytes) 328 { 329 } 330 331 332 void 333 PackageManager::ProgressPackageDownloadComplete(const char* packageName) 334 { 335 } 336 337 338 void 339 PackageManager::ProgressPackageChecksumStarted(const char* title) 340 { 341 } 342 343 344 void 345 PackageManager::ProgressPackageChecksumComplete(const char* title) 346 { 347 } 348 349 350 void 351 PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository) 352 { 353 } 354 355 356 void 357 PackageManager::ProgressTransactionCommitted(InstalledRepository& repository, 358 const BCommitTransactionResult& result) 359 { 360 } 361 362 363 void 364 PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) 365 { 366 } 367 368 369 void 370 PackageManager::JobFailed(BSupportKit::BJob* job) 371 { 372 // TODO:... 373 } 374 375 376 void 377 PackageManager::JobAborted(BSupportKit::BJob* job) 378 { 379 // TODO:... 380 } 381 382 383 bool 384 PackageManager::_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 396 BSolverPackage* 397 PackageManager::_SolverPackageFor(Package* package) const 398 { 399 SolverPackageMap::const_iterator it = fSolverPackages.find(package); 400 return it != fSolverPackages.end() ? it->second : NULL; 401 } 402 403 404 void 405 PackageManager::_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