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