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