1 /* 2 * Copyright 2013-2015, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 * Rene Gollent <rene@gollent.com> 8 */ 9 10 11 #include <package/manager/PackageManager.h> 12 13 #include <Directory.h> 14 #include <package/CommitTransactionResult.h> 15 #include <package/DownloadFileRequest.h> 16 #include <package/PackageRoster.h> 17 #include <package/RefreshRepositoryRequest.h> 18 #include <package/RepositoryCache.h> 19 #include <package/solver/SolverPackage.h> 20 #include <package/solver/SolverPackageSpecifier.h> 21 #include <package/solver/SolverPackageSpecifierList.h> 22 #include <package/solver/SolverProblem.h> 23 #include <package/solver/SolverProblemSolution.h> 24 #include <package/solver/SolverResult.h> 25 26 #include <CopyEngine.h> 27 #include <package/ActivationTransaction.h> 28 #include <package/DaemonClient.h> 29 #include <package/FetchFileJob.h> 30 #include <package/manager/RepositoryBuilder.h> 31 #include <package/ValidateChecksumJob.h> 32 33 #include "PackageManagerUtils.h" 34 35 36 using BPackageKit::BPrivate::FetchFileJob; 37 using BPackageKit::BPrivate::ValidateChecksumJob; 38 39 40 namespace BPackageKit { 41 42 namespace BManager { 43 44 namespace BPrivate { 45 46 47 // #pragma mark - BPackageManager 48 49 50 BPackageManager::BPackageManager(BPackageInstallationLocation location, 51 InstallationInterface* installationInterface, 52 UserInteractionHandler* userInteractionHandler) 53 : 54 fDebugLevel(0), 55 fLocation(location), 56 fSolver(NULL), 57 fSystemRepository(new (std::nothrow) InstalledRepository("system", 58 B_PACKAGE_INSTALLATION_LOCATION_SYSTEM, -1)), 59 fHomeRepository(new (std::nothrow) InstalledRepository("home", 60 B_PACKAGE_INSTALLATION_LOCATION_HOME, -3)), 61 fInstalledRepositories(10), 62 fOtherRepositories(10, true), 63 fLocalRepository(new (std::nothrow) MiscLocalRepository), 64 fTransactions(5, true), 65 fInstallationInterface(installationInterface), 66 fUserInteractionHandler(userInteractionHandler) 67 { 68 } 69 70 71 BPackageManager::~BPackageManager() 72 { 73 delete fSolver; 74 delete fSystemRepository; 75 delete fHomeRepository; 76 delete fLocalRepository; 77 } 78 79 80 void 81 BPackageManager::Init(uint32 flags) 82 { 83 if (fSolver != NULL) 84 return; 85 86 // create the solver 87 status_t error = BSolver::Create(fSolver); 88 if (error != B_OK) 89 DIE(error, "Failed to create solver"); 90 91 if (fSystemRepository == NULL || fHomeRepository == NULL 92 || fLocalRepository == NULL) { 93 throw std::bad_alloc(); 94 } 95 96 fSolver->SetDebugLevel(fDebugLevel); 97 98 BRepositoryBuilder(*fLocalRepository).AddToSolver(fSolver, false); 99 100 // add installation location repositories 101 if ((flags & B_ADD_INSTALLED_REPOSITORIES) != 0) { 102 // We add only the repository of our actual installation location as the 103 // "installed" repository. The repositories for the more general 104 // installation locations are added as regular repositories, but with 105 // better priorities than the actual (remote) repositories. This 106 // prevents the solver from showing conflicts when a package in a more 107 // specific installation location overrides a package in a more general 108 // one. Instead any requirement that is already installed in a more 109 // general installation location will turn up as to be installed as 110 // well. But we can easily filter those out. 111 _AddInstalledRepository(fSystemRepository); 112 113 if (!fSystemRepository->IsInstalled()) { 114 // Only add the home repository if the directory exists 115 BPath path; 116 status_t error = find_directory(B_USER_PACKAGES_DIRECTORY, &path); 117 if (error == B_OK && BEntry(path.Path()).Exists()) 118 _AddInstalledRepository(fHomeRepository); 119 } 120 } 121 122 // add other repositories 123 if ((flags & B_ADD_REMOTE_REPOSITORIES) != 0) { 124 BPackageRoster roster; 125 BStringList repositoryNames; 126 error = roster.GetRepositoryNames(repositoryNames); 127 if (error != B_OK) { 128 fUserInteractionHandler->Warn(error, 129 "Failed to get repository names"); 130 } 131 132 int32 repositoryNameCount = repositoryNames.CountStrings(); 133 for (int32 i = 0; i < repositoryNameCount; i++) { 134 _AddRemoteRepository(roster, repositoryNames.StringAt(i), 135 (flags & B_REFRESH_REPOSITORIES) != 0); 136 } 137 } 138 } 139 140 141 void 142 BPackageManager::SetDebugLevel(int32 level) 143 { 144 fDebugLevel = level; 145 146 if (fSolver != NULL) 147 fSolver->SetDebugLevel(fDebugLevel); 148 } 149 150 151 void 152 BPackageManager::Install(const char* const* packages, int packageCount) 153 { 154 BSolverPackageSpecifierList packagesToInstall; 155 _AddPackageSpecifiers(packages, packageCount, packagesToInstall); 156 Install(packagesToInstall); 157 } 158 159 160 void 161 BPackageManager::Install(const BSolverPackageSpecifierList& packages) 162 { 163 Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES 164 | B_REFRESH_REPOSITORIES); 165 166 // solve 167 const BSolverPackageSpecifier* unmatchedSpecifier; 168 status_t error = fSolver->Install(packages, &unmatchedSpecifier); 169 if (error != B_OK) { 170 if (unmatchedSpecifier != NULL) { 171 DIE(error, "Failed to find a match for \"%s\"", 172 unmatchedSpecifier->SelectString().String()); 173 } else 174 DIE(error, "Failed to compute packages to install"); 175 } 176 177 _HandleProblems(); 178 179 // install/uninstall packages 180 _AnalyzeResult(); 181 _ConfirmChanges(); 182 _ApplyPackageChanges(); 183 } 184 185 186 void 187 BPackageManager::Uninstall(const char* const* packages, int packageCount) 188 { 189 BSolverPackageSpecifierList packagesToUninstall; 190 if (!packagesToUninstall.AppendSpecifiers(packages, packageCount)) 191 throw std::bad_alloc(); 192 Uninstall(packagesToUninstall); 193 } 194 195 196 void 197 BPackageManager::Uninstall(const BSolverPackageSpecifierList& packages) 198 { 199 Init(B_ADD_INSTALLED_REPOSITORIES); 200 201 // find the packages that match the specification 202 const BSolverPackageSpecifier* unmatchedSpecifier; 203 PackageList foundPackages; 204 status_t error = fSolver->FindPackages(packages, 205 BSolver::B_FIND_INSTALLED_ONLY, foundPackages, &unmatchedSpecifier); 206 if (error != B_OK) { 207 if (unmatchedSpecifier != NULL) { 208 DIE(error, "Failed to find a match for \"%s\"", 209 unmatchedSpecifier->SelectString().String()); 210 } else 211 DIE(error, "Failed to compute packages to uninstall"); 212 } 213 214 // determine the inverse base package closure for the found packages 215 // TODO: Optimize! 216 InstalledRepository& installationRepository = InstallationRepository(); 217 bool foundAnotherPackage; 218 do { 219 foundAnotherPackage = false; 220 int32 count = installationRepository.CountPackages(); 221 for (int32 i = 0; i < count; i++) { 222 BSolverPackage* package = installationRepository.PackageAt(i); 223 if (foundPackages.HasItem(package)) 224 continue; 225 226 if (_FindBasePackage(foundPackages, package->Info()) >= 0) { 227 foundPackages.AddItem(package); 228 foundAnotherPackage = true; 229 } 230 } 231 } while (foundAnotherPackage); 232 233 // remove the packages from the repository 234 for (int32 i = 0; BSolverPackage* package = foundPackages.ItemAt(i); i++) 235 installationRepository.DisablePackage(package); 236 237 for (;;) { 238 error = fSolver->VerifyInstallation(BSolver::B_VERIFY_ALLOW_UNINSTALL); 239 if (error != B_OK) 240 DIE(error, "Failed to compute packages to uninstall"); 241 242 _HandleProblems(); 243 244 // (virtually) apply the result to this repository 245 _AnalyzeResult(); 246 247 for (int32 i = foundPackages.CountItems() - 1; i >= 0; i--) { 248 if (!installationRepository.PackagesToDeactivate() 249 .AddItem(foundPackages.ItemAt(i))) { 250 throw std::bad_alloc(); 251 } 252 } 253 254 installationRepository.ApplyChanges(); 255 256 // verify the next specific respository 257 if (!_NextSpecificInstallationLocation()) 258 break; 259 260 foundPackages.MakeEmpty(); 261 262 // NOTE: In theory, after verifying a more specific location, it would 263 // be more correct to compute the inverse base package closure for the 264 // packages we need to uninstall and (if anything changed) verify again. 265 // In practice, however, base packages are always required with an exact 266 // version (ATM). If that base package still exist in a more general 267 // location (the only reason why the package requiring the base package 268 // wouldn't be marked to be uninstalled as well) there shouldn't have 269 // been any reason to remove it from the more specific location in the 270 // first place. 271 } 272 273 _ConfirmChanges(true); 274 _ApplyPackageChanges(true); 275 } 276 277 278 void 279 BPackageManager::Update(const char* const* packages, int packageCount) 280 { 281 BSolverPackageSpecifierList packagesToUpdate; 282 _AddPackageSpecifiers(packages, packageCount, packagesToUpdate); 283 Update(packagesToUpdate); 284 } 285 286 287 void 288 BPackageManager::Update(const BSolverPackageSpecifierList& packages) 289 { 290 Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES 291 | B_REFRESH_REPOSITORIES); 292 293 // solve 294 const BSolverPackageSpecifier* unmatchedSpecifier; 295 status_t error = fSolver->Update(packages, true, 296 &unmatchedSpecifier); 297 if (error != B_OK) { 298 if (unmatchedSpecifier != NULL) { 299 DIE(error, "Failed to find a match for \"%s\"", 300 unmatchedSpecifier->SelectString().String()); 301 } else 302 DIE(error, "Failed to compute packages to update"); 303 } 304 305 _HandleProblems(); 306 307 // install/uninstall packages 308 _AnalyzeResult(); 309 _ConfirmChanges(); 310 _ApplyPackageChanges(); 311 } 312 313 314 void 315 BPackageManager::FullSync() 316 { 317 Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES 318 | B_REFRESH_REPOSITORIES); 319 320 // solve 321 status_t error = fSolver->FullSync(); 322 if (error != B_OK) 323 DIE(error, "Failed to compute packages to synchronize"); 324 325 _HandleProblems(); 326 327 // install/uninstall packages 328 _AnalyzeResult(); 329 _ConfirmChanges(); 330 _ApplyPackageChanges(); 331 } 332 333 334 void 335 BPackageManager::VerifyInstallation() 336 { 337 Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES 338 | B_REFRESH_REPOSITORIES); 339 340 for (;;) { 341 status_t error = fSolver->VerifyInstallation(); 342 if (error != B_OK) 343 DIE(error, "Failed to compute package dependencies"); 344 345 _HandleProblems(); 346 347 // (virtually) apply the result to this repository 348 _AnalyzeResult(); 349 InstallationRepository().ApplyChanges(); 350 351 // verify the next specific respository 352 if (!_NextSpecificInstallationLocation()) 353 break; 354 } 355 356 _ConfirmChanges(); 357 _ApplyPackageChanges(); 358 } 359 360 361 BPackageManager::InstalledRepository& 362 BPackageManager::InstallationRepository() 363 { 364 if (fInstalledRepositories.IsEmpty()) 365 DIE("No installation repository"); 366 367 return *fInstalledRepositories.LastItem(); 368 } 369 370 371 void 372 BPackageManager::JobStarted(BSupportKit::BJob* job) 373 { 374 if (dynamic_cast<FetchFileJob*>(job) != NULL) { 375 FetchFileJob* fetchJob = (FetchFileJob*)job; 376 fUserInteractionHandler->ProgressPackageDownloadStarted( 377 fetchJob->DownloadFileName()); 378 } else if (dynamic_cast<ValidateChecksumJob*>(job) != NULL) { 379 fUserInteractionHandler->ProgressPackageChecksumStarted( 380 job->Title().String()); 381 } 382 } 383 384 385 void 386 BPackageManager::JobProgress(BSupportKit::BJob* job) 387 { 388 if (dynamic_cast<FetchFileJob*>(job) != NULL) { 389 FetchFileJob* fetchJob = (FetchFileJob*)job; 390 fUserInteractionHandler->ProgressPackageDownloadActive( 391 fetchJob->DownloadFileName(), fetchJob->DownloadProgress(), 392 fetchJob->DownloadBytes(), fetchJob->DownloadTotalBytes()); 393 } 394 } 395 396 397 void 398 BPackageManager::JobSucceeded(BSupportKit::BJob* job) 399 { 400 if (dynamic_cast<FetchFileJob*>(job) != NULL) { 401 FetchFileJob* fetchJob = (FetchFileJob*)job; 402 fUserInteractionHandler->ProgressPackageDownloadComplete( 403 fetchJob->DownloadFileName()); 404 } else if (dynamic_cast<ValidateChecksumJob*>(job) != NULL) { 405 fUserInteractionHandler->ProgressPackageChecksumComplete( 406 job->Title().String()); 407 } 408 } 409 410 411 void 412 BPackageManager::_HandleProblems() 413 { 414 while (fSolver->HasProblems()) { 415 fUserInteractionHandler->HandleProblems(); 416 417 status_t error = fSolver->SolveAgain(); 418 if (error != B_OK) 419 DIE(error, "Failed to recompute packages to un/-install"); 420 } 421 } 422 423 424 void 425 BPackageManager::_AnalyzeResult() 426 { 427 BSolverResult result; 428 status_t error = fSolver->GetResult(result); 429 if (error != B_OK) 430 DIE(error, "Failed to compute packages to un/-install"); 431 432 InstalledRepository& installationRepository = InstallationRepository(); 433 PackageList& packagesToActivate 434 = installationRepository.PackagesToActivate(); 435 PackageList& packagesToDeactivate 436 = installationRepository.PackagesToDeactivate(); 437 438 PackageList potentialBasePackages; 439 440 for (int32 i = 0; const BSolverResultElement* element = result.ElementAt(i); 441 i++) { 442 BSolverPackage* package = element->Package(); 443 444 switch (element->Type()) { 445 case BSolverResultElement::B_TYPE_INSTALL: 446 { 447 PackageList& packageList 448 = dynamic_cast<InstalledRepository*>(package->Repository()) 449 != NULL 450 ? potentialBasePackages 451 : packagesToActivate; 452 if (!packageList.AddItem(package)) 453 throw std::bad_alloc(); 454 break; 455 } 456 457 case BSolverResultElement::B_TYPE_UNINSTALL: 458 if (!packagesToDeactivate.AddItem(package)) 459 throw std::bad_alloc(); 460 break; 461 } 462 } 463 464 // Make sure base packages are installed in the same location. 465 for (int32 i = 0; i < packagesToActivate.CountItems(); i++) { 466 BSolverPackage* package = packagesToActivate.ItemAt(i); 467 int32 index = _FindBasePackage(potentialBasePackages, package->Info()); 468 if (index < 0) 469 continue; 470 471 BSolverPackage* basePackage = potentialBasePackages.RemoveItemAt(index); 472 if (!packagesToActivate.AddItem(basePackage)) 473 throw std::bad_alloc(); 474 } 475 476 fInstallationInterface->ResultComputed(installationRepository); 477 } 478 479 480 void 481 BPackageManager::_ConfirmChanges(bool fromMostSpecific) 482 { 483 // check, if there are any changes at all 484 int32 count = fInstalledRepositories.CountItems(); 485 bool hasChanges = false; 486 for (int32 i = 0; i < count; i++) { 487 if (fInstalledRepositories.ItemAt(i)->HasChanges()) { 488 hasChanges = true; 489 break; 490 } 491 } 492 493 if (!hasChanges) 494 throw BNothingToDoException(); 495 496 fUserInteractionHandler->ConfirmChanges(fromMostSpecific); 497 } 498 499 500 void 501 BPackageManager::_ApplyPackageChanges(bool fromMostSpecific) 502 { 503 int32 count = fInstalledRepositories.CountItems(); 504 if (fromMostSpecific) { 505 for (int32 i = count - 1; i >= 0; i--) 506 _PreparePackageChanges(*fInstalledRepositories.ItemAt(i)); 507 } else { 508 for (int32 i = 0; i < count; i++) 509 _PreparePackageChanges(*fInstalledRepositories.ItemAt(i)); 510 } 511 512 for (int32 i = 0; Transaction* transaction = fTransactions.ItemAt(i); i++) 513 _CommitPackageChanges(*transaction); 514 515 // TODO: Clean up the transaction directories on error! 516 } 517 518 519 void 520 BPackageManager::_PreparePackageChanges( 521 InstalledRepository& installationRepository) 522 { 523 if (!installationRepository.HasChanges()) 524 return; 525 526 PackageList& packagesToActivate 527 = installationRepository.PackagesToActivate(); 528 PackageList& packagesToDeactivate 529 = installationRepository.PackagesToDeactivate(); 530 531 // create the transaction 532 Transaction* transaction = new Transaction(installationRepository); 533 if (!fTransactions.AddItem(transaction)) { 534 delete transaction; 535 throw std::bad_alloc(); 536 } 537 538 status_t error = fInstallationInterface->PrepareTransaction(*transaction); 539 if (error != B_OK) 540 DIE(error, "Failed to create transaction"); 541 542 // download the new packages and prepare the transaction 543 for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i); 544 i++) { 545 // get package URL and target entry 546 547 BString fileName(package->Info().FileName()); 548 if (fileName.IsEmpty()) 549 throw std::bad_alloc(); 550 551 BEntry entry; 552 error = entry.SetTo(&transaction->TransactionDirectory(), fileName); 553 if (error != B_OK) 554 DIE(error, "Failed to create package entry"); 555 556 RemoteRepository* remoteRepository 557 = dynamic_cast<RemoteRepository*>(package->Repository()); 558 if (remoteRepository != NULL) { 559 // download the package 560 BString url = remoteRepository->Config().PackagesURL(); 561 url << '/' << fileName; 562 563 status_t error = DownloadPackage(url, entry, 564 package->Info().Checksum()); 565 if (error != B_OK) 566 DIE(error, "Failed to download package %s", 567 package->Info().Name().String()); 568 } else if (package->Repository() != &installationRepository) { 569 // clone the existing package 570 LocalRepository* localRepository 571 = dynamic_cast<LocalRepository*>(package->Repository()); 572 if (localRepository == NULL) { 573 DIE("Internal error: repository %s is not a local repository", 574 package->Repository()->Name().String()); 575 } 576 _ClonePackageFile(localRepository, package, entry); 577 } 578 579 // add package to transaction 580 if (!transaction->ActivationTransaction().AddPackageToActivate( 581 fileName)) { 582 throw std::bad_alloc(); 583 } 584 } 585 586 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); 587 i++) { 588 // add package to transaction 589 if (!transaction->ActivationTransaction().AddPackageToDeactivate( 590 package->Info().FileName())) { 591 throw std::bad_alloc(); 592 } 593 } 594 } 595 596 597 void 598 BPackageManager::_CommitPackageChanges(Transaction& transaction) 599 { 600 InstalledRepository& installationRepository = transaction.Repository(); 601 602 fUserInteractionHandler->ProgressStartApplyingChanges( 603 installationRepository); 604 605 // commit the transaction 606 BCommitTransactionResult transactionResult; 607 status_t error = fInstallationInterface->CommitTransaction(transaction, 608 transactionResult); 609 if (error != B_OK) 610 DIE(error, "Failed to commit transaction"); 611 if (transactionResult.Error() != B_TRANSACTION_OK) 612 DIE(transactionResult); 613 614 fUserInteractionHandler->ProgressTransactionCommitted( 615 installationRepository, transactionResult); 616 617 BEntry transactionDirectoryEntry; 618 if ((error = transaction.TransactionDirectory() 619 .GetEntry(&transactionDirectoryEntry)) != B_OK 620 || (error = transactionDirectoryEntry.Remove()) != B_OK) { 621 fUserInteractionHandler->Warn(error, 622 "Failed to remove transaction directory"); 623 } 624 625 fUserInteractionHandler->ProgressApplyingChangesDone( 626 installationRepository); 627 } 628 629 630 void 631 BPackageManager::_ClonePackageFile(LocalRepository* repository, 632 BSolverPackage* package, const BEntry& entry) 633 { 634 // get source and destination path 635 BPath sourcePath; 636 repository->GetPackagePath(package, sourcePath); 637 638 BPath destinationPath; 639 status_t error = entry.GetPath(&destinationPath); 640 if (error != B_OK) { 641 DIE(error, "Failed to entry path of package file to install \"%s\"", 642 package->Info().FileName().String()); 643 } 644 645 // Copy the package. Ideally we would just hard-link it, but BFS doesn't 646 // support that. 647 error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path()); 648 if (error != B_OK) 649 DIE(error, "Failed to copy package file \"%s\"", sourcePath.Path()); 650 } 651 652 653 int32 654 BPackageManager::_FindBasePackage(const PackageList& packages, 655 const BPackageInfo& info) 656 { 657 if (info.BasePackage().IsEmpty()) 658 return -1; 659 660 // find the requirement matching the base package 661 BPackageResolvableExpression* basePackage = NULL; 662 int32 count = info.RequiresList().CountItems(); 663 for (int32 i = 0; i < count; i++) { 664 BPackageResolvableExpression* requires = info.RequiresList().ItemAt(i); 665 if (requires->Name() == info.BasePackage()) { 666 basePackage = requires; 667 break; 668 } 669 } 670 671 if (basePackage == NULL) { 672 fUserInteractionHandler->Warn(B_OK, "Package %s-%s doesn't have a " 673 "matching requires for its base package \"%s\"", 674 info.Name().String(), info.Version().ToString().String(), 675 info.BasePackage().String()); 676 return -1; 677 } 678 679 // find the first package matching the base package requires 680 count = packages.CountItems(); 681 for (int32 i = 0; i < count; i++) { 682 BSolverPackage* package = packages.ItemAt(i); 683 if (package->Name() == basePackage->Name() 684 && package->Info().Matches(*basePackage)) { 685 return i; 686 } 687 } 688 689 return -1; 690 } 691 692 693 void 694 BPackageManager::_AddInstalledRepository(InstalledRepository* repository) 695 { 696 fInstallationInterface->InitInstalledRepository(*repository); 697 698 BRepositoryBuilder(*repository) 699 .AddToSolver(fSolver, repository->Location() == fLocation); 700 repository->SetPriority(repository->InitialPriority()); 701 702 if (!fInstalledRepositories.AddItem(repository)) 703 throw std::bad_alloc(); 704 } 705 706 707 void 708 BPackageManager::_AddRemoteRepository(BPackageRoster& roster, const char* name, 709 bool refresh) 710 { 711 BRepositoryConfig config; 712 status_t error = roster.GetRepositoryConfig(name, &config); 713 if (error != B_OK) { 714 fUserInteractionHandler->Warn(error, 715 "Failed to get config for repository \"%s\". Skipping.", name); 716 return; 717 } 718 719 BRepositoryCache cache; 720 error = _GetRepositoryCache(roster, config, refresh, cache); 721 if (error != B_OK) { 722 fUserInteractionHandler->Warn(error, 723 "Failed to get cache for repository \"%s\". Skipping.", name); 724 return; 725 } 726 727 RemoteRepository* repository = new RemoteRepository(config); 728 if (!fOtherRepositories.AddItem(repository)) { 729 delete repository; 730 throw std::bad_alloc(); 731 } 732 733 BRepositoryBuilder(*repository, cache, config.Name()) 734 .AddToSolver(fSolver, false); 735 } 736 737 738 status_t 739 BPackageManager::_GetRepositoryCache(BPackageRoster& roster, 740 const BRepositoryConfig& config, bool refresh, BRepositoryCache& _cache) 741 { 742 if (!refresh && roster.GetRepositoryCache(config.Name(), &_cache) == B_OK) 743 return B_OK; 744 745 status_t error = RefreshRepository(config); 746 if (error != B_OK) { 747 fUserInteractionHandler->Warn(error, 748 "Refreshing repository \"%s\" failed", config.Name().String()); 749 } 750 751 return roster.GetRepositoryCache(config.Name(), &_cache); 752 } 753 754 755 void 756 BPackageManager::_AddPackageSpecifiers(const char* const* searchStrings, 757 int searchStringCount, BSolverPackageSpecifierList& specifierList) 758 { 759 for (int i = 0; i < searchStringCount; i++) { 760 const char* searchString = searchStrings[i]; 761 if (_IsLocalPackage(searchString)) { 762 BSolverPackage* package = _AddLocalPackage(searchString); 763 if (!specifierList.AppendSpecifier(package)) 764 throw std::bad_alloc(); 765 } else { 766 if (!specifierList.AppendSpecifier(searchString)) 767 throw std::bad_alloc(); 768 } 769 } 770 } 771 772 773 bool 774 BPackageManager::_IsLocalPackage(const char* fileName) 775 { 776 // Simple heuristic: fileName contains ".hpkg" and there's actually a file 777 // it refers to. 778 struct stat st; 779 return strstr(fileName, ".hpkg") != NULL && stat(fileName, &st) == 0 780 && S_ISREG(st.st_mode); 781 } 782 783 784 BSolverPackage* 785 BPackageManager::_AddLocalPackage(const char* fileName) 786 { 787 if (fLocalRepository == NULL) 788 throw std::bad_alloc(); 789 return fLocalRepository->AddLocalPackage(fileName); 790 } 791 792 793 bool 794 BPackageManager::_NextSpecificInstallationLocation() 795 { 796 try { 797 if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) { 798 fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME; 799 fSystemRepository->SetInstalled(false); 800 _AddInstalledRepository(fHomeRepository); 801 return true; 802 } 803 } catch (BFatalErrorException& e) { 804 // No home repo. This is acceptable for example when we are in an haikuporter chroot. 805 } 806 807 return false; 808 } 809 810 811 status_t 812 BPackageManager::DownloadPackage(const BString& fileURL, 813 const BEntry& targetEntry, const BString& checksum) 814 { 815 BDecisionProvider provider; 816 BContext context(provider, *this); 817 return DownloadFileRequest(context, fileURL, targetEntry, checksum) 818 .Process(); 819 } 820 821 822 status_t 823 BPackageManager::RefreshRepository(const BRepositoryConfig& repoConfig) 824 { 825 BDecisionProvider provider; 826 BContext context(provider, *this); 827 return BRefreshRepositoryRequest(context, repoConfig).Process(); 828 } 829 830 831 // #pragma mark - RemoteRepository 832 833 834 BPackageManager::RemoteRepository::RemoteRepository( 835 const BRepositoryConfig& config) 836 : 837 BSolverRepository(), 838 fConfig(config) 839 { 840 } 841 842 843 const BRepositoryConfig& 844 BPackageManager::RemoteRepository::Config() const 845 { 846 return fConfig; 847 } 848 849 850 // #pragma mark - LocalRepository 851 852 853 BPackageManager::LocalRepository::LocalRepository() 854 : 855 BSolverRepository() 856 { 857 } 858 859 860 BPackageManager::LocalRepository::LocalRepository(const BString& name) 861 : 862 BSolverRepository(name) 863 { 864 } 865 866 867 // #pragma mark - MiscLocalRepository 868 869 870 BPackageManager::MiscLocalRepository::MiscLocalRepository() 871 : 872 LocalRepository("local"), 873 fPackagePaths() 874 { 875 SetPriority(-127); 876 } 877 878 879 BSolverPackage* 880 BPackageManager::MiscLocalRepository::AddLocalPackage(const char* fileName) 881 { 882 BSolverPackage* package; 883 BRepositoryBuilder(*this).AddPackage(fileName, &package); 884 885 fPackagePaths[package] = fileName; 886 887 return package; 888 } 889 890 891 void 892 BPackageManager::MiscLocalRepository::GetPackagePath(BSolverPackage* package, 893 BPath& _path) 894 { 895 PackagePathMap::const_iterator it = fPackagePaths.find(package); 896 if (it == fPackagePaths.end()) { 897 DIE("Package %s not in local repository", 898 package->VersionedName().String()); 899 } 900 901 status_t error = _path.SetTo(it->second.c_str()); 902 if (error != B_OK) 903 DIE(error, "Failed to init package path %s", it->second.c_str()); 904 } 905 906 907 // #pragma mark - InstalledRepository 908 909 910 BPackageManager::InstalledRepository::InstalledRepository(const char* name, 911 BPackageInstallationLocation location, int32 priority) 912 : 913 LocalRepository(), 914 fDisabledPackages(10, true), 915 fPackagesToActivate(), 916 fPackagesToDeactivate(), 917 fInitialName(name), 918 fLocation(location), 919 fInitialPriority(priority) 920 { 921 } 922 923 924 void 925 BPackageManager::InstalledRepository::GetPackagePath(BSolverPackage* package, 926 BPath& _path) 927 { 928 directory_which packagesWhich; 929 switch (fLocation) { 930 case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM: 931 packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY; 932 break; 933 case B_PACKAGE_INSTALLATION_LOCATION_HOME: 934 packagesWhich = B_USER_PACKAGES_DIRECTORY; 935 break; 936 default: 937 DIE("Don't know packages directory path for installation location " 938 "\"%s\"", Name().String()); 939 } 940 941 BString fileName(package->Info().FileName()); 942 status_t error = find_directory(packagesWhich, &_path); 943 if (error != B_OK || (error = _path.Append(fileName)) != B_OK) { 944 DIE(error, "Failed to get path of package file \"%s\" in installation " 945 "location \"%s\"", fileName.String(), Name().String()); 946 } 947 } 948 949 950 void 951 BPackageManager::InstalledRepository::DisablePackage(BSolverPackage* package) 952 { 953 if (fDisabledPackages.HasItem(package)) 954 DIE("Package %s already disabled", package->VersionedName().String()); 955 956 if (package->Repository() != this) { 957 DIE("Package %s not in repository %s", 958 package->VersionedName().String(), Name().String()); 959 } 960 961 // move to disabled list 962 if (!fDisabledPackages.AddItem(package)) 963 throw std::bad_alloc(); 964 965 RemovePackage(package); 966 } 967 968 969 bool 970 BPackageManager::InstalledRepository::EnablePackage(BSolverPackage* package) 971 { 972 return fDisabledPackages.RemoveItem(package); 973 } 974 975 976 bool 977 BPackageManager::InstalledRepository::HasChanges() const 978 { 979 return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty(); 980 } 981 982 983 void 984 BPackageManager::InstalledRepository::ApplyChanges() 985 { 986 // disable packages to deactivate 987 for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i); 988 i++) { 989 if (!fDisabledPackages.HasItem(package)) 990 DisablePackage(package); 991 } 992 993 // add packages to activate 994 for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i); 995 i++) { 996 status_t error = AddPackage(package->Info()); 997 if (error != B_OK) { 998 DIE(error, "Failed to add package %s to %s repository", 999 package->Name().String(), Name().String()); 1000 } 1001 } 1002 } 1003 1004 1005 // #pragma mark - Transaction 1006 1007 1008 BPackageManager::Transaction::Transaction(InstalledRepository& repository) 1009 : 1010 fRepository(repository), 1011 fTransaction(), 1012 fTransactionDirectory() 1013 { 1014 } 1015 1016 1017 BPackageManager::Transaction::~Transaction() 1018 { 1019 } 1020 1021 1022 // #pragma mark - InstallationInterface 1023 1024 1025 BPackageManager::InstallationInterface::~InstallationInterface() 1026 { 1027 } 1028 1029 1030 void 1031 BPackageManager::InstallationInterface::ResultComputed( 1032 InstalledRepository& repository) 1033 { 1034 } 1035 1036 1037 // #pragma mark - ClientInstallationInterface 1038 1039 1040 BPackageManager::ClientInstallationInterface::ClientInstallationInterface() 1041 : 1042 fDaemonClient() 1043 { 1044 } 1045 1046 1047 BPackageManager::ClientInstallationInterface::~ClientInstallationInterface() 1048 { 1049 } 1050 1051 1052 void 1053 BPackageManager::ClientInstallationInterface::InitInstalledRepository( 1054 InstalledRepository& repository) 1055 { 1056 const char* name = repository.InitialName(); 1057 BRepositoryBuilder(repository, name) 1058 .AddPackages(repository.Location(), name); 1059 } 1060 1061 1062 status_t 1063 BPackageManager::ClientInstallationInterface::PrepareTransaction( 1064 Transaction& transaction) 1065 { 1066 return fDaemonClient.CreateTransaction(transaction.Repository().Location(), 1067 transaction.ActivationTransaction(), 1068 transaction.TransactionDirectory()); 1069 } 1070 1071 1072 status_t 1073 BPackageManager::ClientInstallationInterface::CommitTransaction( 1074 Transaction& transaction, BCommitTransactionResult& _result) 1075 { 1076 return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(), 1077 _result); 1078 } 1079 1080 1081 // #pragma mark - UserInteractionHandler 1082 1083 1084 BPackageManager::UserInteractionHandler::~UserInteractionHandler() 1085 { 1086 } 1087 1088 1089 void 1090 BPackageManager::UserInteractionHandler::HandleProblems() 1091 { 1092 throw BAbortedByUserException(); 1093 } 1094 1095 1096 void 1097 BPackageManager::UserInteractionHandler::ConfirmChanges(bool fromMostSpecific) 1098 { 1099 throw BAbortedByUserException(); 1100 } 1101 1102 1103 void 1104 BPackageManager::UserInteractionHandler::Warn(status_t error, 1105 const char* format, ...) 1106 { 1107 } 1108 1109 1110 void 1111 BPackageManager::UserInteractionHandler::ProgressPackageDownloadStarted( 1112 const char* packageName) 1113 { 1114 } 1115 1116 1117 void 1118 BPackageManager::UserInteractionHandler::ProgressPackageDownloadActive( 1119 const char* packageName, float completionPercentage, off_t bytes, 1120 off_t totalBytes) 1121 { 1122 } 1123 1124 1125 void 1126 BPackageManager::UserInteractionHandler::ProgressPackageDownloadComplete( 1127 const char* packageName) 1128 { 1129 } 1130 1131 1132 void 1133 BPackageManager::UserInteractionHandler::ProgressPackageChecksumStarted( 1134 const char* title) 1135 { 1136 } 1137 1138 1139 void 1140 BPackageManager::UserInteractionHandler::ProgressPackageChecksumComplete( 1141 const char* title) 1142 { 1143 } 1144 1145 1146 void 1147 BPackageManager::UserInteractionHandler::ProgressStartApplyingChanges( 1148 InstalledRepository& repository) 1149 { 1150 } 1151 1152 1153 void 1154 BPackageManager::UserInteractionHandler::ProgressTransactionCommitted( 1155 InstalledRepository& repository, const BCommitTransactionResult& result) 1156 { 1157 } 1158 1159 1160 void 1161 BPackageManager::UserInteractionHandler::ProgressApplyingChangesDone( 1162 InstalledRepository& repository) 1163 { 1164 } 1165 1166 1167 } // namespace BPrivate 1168 1169 } // namespace BManager 1170 1171 } // namespace BPackageKit 1172