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 using BPackageKit::BPrivate::FetchUtils; 38 #include "PackageManagerUtils.h" 39 40 #undef B_TRANSLATION_CONTEXT 41 #define B_TRANSLATION_CONTEXT "PackageManagerKit" 42 43 44 using BPackageKit::BPrivate::FetchFileJob; 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 reusingDownload = 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 reusingDownload = true; 597 printf("Re-using download '%s' from previous " 598 "transaction%s\n", bestFile, 599 FetchUtils::IsDownloadCompleted( 600 path.Path()) ? "" : " (partial)"); 601 } 602 globfree(&globbuf); 603 } 604 } 605 606 // download the package (this will resume the download if the 607 // file already exists) 608 BString url = remoteRepository->Config().PackagesURL(); 609 url << '/' << fileName; 610 611 status_t error; 612 retryDownload: 613 error = DownloadPackage(url, entry, 614 package->Info().Checksum()); 615 if (error != B_OK) { 616 if (error == B_BAD_DATA || error == ERANGE) { 617 // B_BAD_DATA is returned when there is a checksum 618 // mismatch. Make sure this download is not re-used. 619 entry.Remove(); 620 621 if (reusingDownload) { 622 // Maybe the download we reused had some problem. 623 // Try again, this time without reusing the download. 624 printf("\nPrevious download '%s' was invalid. Redownloading.\n", 625 path.Path()); 626 reusingDownload = false; 627 goto retryDownload; 628 } 629 } 630 DIE(error, "Failed to download package %s", 631 package->Info().Name().String()); 632 } 633 } else if (package->Repository() != &installationRepository) { 634 // clone the existing package 635 LocalRepository* localRepository 636 = dynamic_cast<LocalRepository*>(package->Repository()); 637 if (localRepository == NULL) { 638 DIE("Internal error: repository %s is not a local repository", 639 package->Repository()->Name().String()); 640 } 641 _ClonePackageFile(localRepository, package, entry); 642 } 643 644 // add package to transaction 645 if (!transaction->ActivationTransaction().AddPackageToActivate( 646 fileName)) { 647 throw std::bad_alloc(); 648 } 649 } 650 651 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i); 652 i++) { 653 // add package to transaction 654 if (!transaction->ActivationTransaction().AddPackageToDeactivate( 655 package->Info().FileName())) { 656 throw std::bad_alloc(); 657 } 658 } 659 } 660 661 662 void 663 BPackageManager::_CommitPackageChanges(Transaction& transaction) 664 { 665 InstalledRepository& installationRepository = transaction.Repository(); 666 667 fUserInteractionHandler->ProgressStartApplyingChanges( 668 installationRepository); 669 670 // commit the transaction 671 BCommitTransactionResult transactionResult; 672 status_t error = fInstallationInterface->CommitTransaction(transaction, 673 transactionResult); 674 if (error != B_OK) 675 DIE(error, "Failed to commit transaction"); 676 if (transactionResult.Error() != B_TRANSACTION_OK) 677 DIE(transactionResult); 678 679 fUserInteractionHandler->ProgressTransactionCommitted( 680 installationRepository, transactionResult); 681 682 BEntry transactionDirectoryEntry; 683 if ((error = transaction.TransactionDirectory() 684 .GetEntry(&transactionDirectoryEntry)) != B_OK 685 || (error = transactionDirectoryEntry.Remove()) != B_OK) { 686 fUserInteractionHandler->Warn(error, 687 B_TRANSLATE("Failed to remove transaction directory")); 688 } 689 690 fUserInteractionHandler->ProgressApplyingChangesDone( 691 installationRepository); 692 } 693 694 695 void 696 BPackageManager::_ClonePackageFile(LocalRepository* repository, 697 BSolverPackage* package, const BEntry& entry) 698 { 699 // get source and destination path 700 BPath sourcePath; 701 repository->GetPackagePath(package, sourcePath); 702 703 BPath destinationPath; 704 status_t error = entry.GetPath(&destinationPath); 705 if (error != B_OK) { 706 DIE(error, "Failed to entry path of package file to install \"%s\"", 707 package->Info().FileName().String()); 708 } 709 710 // Copy the package. Ideally we would just hard-link it, but BFS doesn't 711 // support that. 712 error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path()); 713 if (error != B_OK) 714 DIE(error, "Failed to copy package file \"%s\"", sourcePath.Path()); 715 } 716 717 718 int32 719 BPackageManager::_FindBasePackage(const PackageList& packages, 720 const BPackageInfo& info) 721 { 722 if (info.BasePackage().IsEmpty()) 723 return -1; 724 725 // find the requirement matching the base package 726 BPackageResolvableExpression* basePackage = NULL; 727 int32 count = info.RequiresList().CountItems(); 728 for (int32 i = 0; i < count; i++) { 729 BPackageResolvableExpression* requires = info.RequiresList().ItemAt(i); 730 if (requires->Name() == info.BasePackage()) { 731 basePackage = requires; 732 break; 733 } 734 } 735 736 if (basePackage == NULL) { 737 fUserInteractionHandler->Warn(B_OK, B_TRANSLATE("Package %s-%s " 738 "doesn't have a matching requires for its base package \"%s\""), 739 info.Name().String(), info.Version().ToString().String(), 740 info.BasePackage().String()); 741 return -1; 742 } 743 744 // find the first package matching the base package requires 745 count = packages.CountItems(); 746 for (int32 i = 0; i < count; i++) { 747 BSolverPackage* package = packages.ItemAt(i); 748 if (package->Name() == basePackage->Name() 749 && package->Info().Matches(*basePackage)) { 750 return i; 751 } 752 } 753 754 return -1; 755 } 756 757 758 void 759 BPackageManager::_AddInstalledRepository(InstalledRepository* repository) 760 { 761 fInstallationInterface->InitInstalledRepository(*repository); 762 763 BRepositoryBuilder(*repository) 764 .AddToSolver(fSolver, repository->Location() == fLocation); 765 repository->SetPriority(repository->InitialPriority()); 766 767 if (!fInstalledRepositories.AddItem(repository)) 768 throw std::bad_alloc(); 769 } 770 771 772 void 773 BPackageManager::_AddRemoteRepository(BPackageRoster& roster, const char* name, 774 bool refresh) 775 { 776 BRepositoryConfig config; 777 status_t error = roster.GetRepositoryConfig(name, &config); 778 if (error != B_OK) { 779 fUserInteractionHandler->Warn(error, B_TRANSLATE( 780 "Failed to get config for repository \"%s\". Skipping."), name); 781 return; 782 } 783 784 BRepositoryCache cache; 785 error = _GetRepositoryCache(roster, config, refresh, cache); 786 if (error != B_OK) { 787 fUserInteractionHandler->Warn(error, B_TRANSLATE( 788 "Failed to get cache for repository \"%s\". Skipping."), name); 789 return; 790 } 791 792 RemoteRepository* repository = new RemoteRepository(config); 793 if (!fOtherRepositories.AddItem(repository)) { 794 delete repository; 795 throw std::bad_alloc(); 796 } 797 798 BRepositoryBuilder(*repository, cache, config.Name()) 799 .AddToSolver(fSolver, false); 800 } 801 802 803 status_t 804 BPackageManager::_GetRepositoryCache(BPackageRoster& roster, 805 const BRepositoryConfig& config, bool refresh, BRepositoryCache& _cache) 806 { 807 if (!refresh && roster.GetRepositoryCache(config.Name(), &_cache) == B_OK) 808 return B_OK; 809 810 status_t error = RefreshRepository(config); 811 if (error != B_OK) { 812 fUserInteractionHandler->Warn(error, B_TRANSLATE( 813 "Refreshing repository \"%s\" failed"), config.Name().String()); 814 } 815 816 return roster.GetRepositoryCache(config.Name(), &_cache); 817 } 818 819 820 void 821 BPackageManager::_AddPackageSpecifiers(const char* const* searchStrings, 822 int searchStringCount, BSolverPackageSpecifierList& specifierList) 823 { 824 for (int i = 0; i < searchStringCount; i++) { 825 const char* searchString = searchStrings[i]; 826 if (_IsLocalPackage(searchString)) { 827 BSolverPackage* package = _AddLocalPackage(searchString); 828 if (!specifierList.AppendSpecifier(package)) 829 throw std::bad_alloc(); 830 } else { 831 if (!specifierList.AppendSpecifier(searchString)) 832 throw std::bad_alloc(); 833 } 834 } 835 } 836 837 838 bool 839 BPackageManager::_IsLocalPackage(const char* fileName) 840 { 841 // Simple heuristic: fileName contains ".hpkg" and there's actually a file 842 // it refers to. 843 struct stat st; 844 return strstr(fileName, ".hpkg") != NULL && stat(fileName, &st) == 0 845 && S_ISREG(st.st_mode); 846 } 847 848 849 BSolverPackage* 850 BPackageManager::_AddLocalPackage(const char* fileName) 851 { 852 if (fLocalRepository == NULL) 853 throw std::bad_alloc(); 854 return fLocalRepository->AddLocalPackage(fileName); 855 } 856 857 858 bool 859 BPackageManager::_NextSpecificInstallationLocation() 860 { 861 try { 862 if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) { 863 fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME; 864 fSystemRepository->SetInstalled(false); 865 _AddInstalledRepository(fHomeRepository); 866 return true; 867 } 868 } catch (BFatalErrorException& e) { 869 // No home repo. This is acceptable for example when we are in an haikuporter chroot. 870 } 871 872 return false; 873 } 874 875 876 status_t 877 BPackageManager::DownloadPackage(const BString& fileURL, 878 const BEntry& targetEntry, const BString& checksum) 879 { 880 BDecisionProvider provider; 881 BContext context(provider, *this); 882 return DownloadFileRequest(context, fileURL, targetEntry, checksum) 883 .Process(); 884 } 885 886 887 status_t 888 BPackageManager::RefreshRepository(const BRepositoryConfig& repoConfig) 889 { 890 BDecisionProvider provider; 891 BContext context(provider, *this); 892 return BRefreshRepositoryRequest(context, repoConfig).Process(); 893 } 894 895 896 // #pragma mark - RemoteRepository 897 898 899 BPackageManager::RemoteRepository::RemoteRepository( 900 const BRepositoryConfig& config) 901 : 902 BSolverRepository(), 903 fConfig(config) 904 { 905 } 906 907 908 const BRepositoryConfig& 909 BPackageManager::RemoteRepository::Config() const 910 { 911 return fConfig; 912 } 913 914 915 // #pragma mark - LocalRepository 916 917 918 BPackageManager::LocalRepository::LocalRepository() 919 : 920 BSolverRepository() 921 { 922 } 923 924 925 BPackageManager::LocalRepository::LocalRepository(const BString& name) 926 : 927 BSolverRepository(name) 928 { 929 } 930 931 932 // #pragma mark - MiscLocalRepository 933 934 935 BPackageManager::MiscLocalRepository::MiscLocalRepository() 936 : 937 LocalRepository("local"), 938 fPackagePaths() 939 { 940 SetPriority(-127); 941 } 942 943 944 BSolverPackage* 945 BPackageManager::MiscLocalRepository::AddLocalPackage(const char* fileName) 946 { 947 BSolverPackage* package; 948 BRepositoryBuilder(*this).AddPackage(fileName, &package); 949 950 fPackagePaths[package] = fileName; 951 952 return package; 953 } 954 955 956 void 957 BPackageManager::MiscLocalRepository::GetPackagePath(BSolverPackage* package, 958 BPath& _path) 959 { 960 PackagePathMap::const_iterator it = fPackagePaths.find(package); 961 if (it == fPackagePaths.end()) { 962 DIE("Package %s not in local repository", 963 package->VersionedName().String()); 964 } 965 966 status_t error = _path.SetTo(it->second.c_str()); 967 if (error != B_OK) 968 DIE(error, "Failed to init package path %s", it->second.c_str()); 969 } 970 971 972 // #pragma mark - InstalledRepository 973 974 975 BPackageManager::InstalledRepository::InstalledRepository(const char* name, 976 BPackageInstallationLocation location, int32 priority) 977 : 978 LocalRepository(), 979 fDisabledPackages(10, true), 980 fPackagesToActivate(), 981 fPackagesToDeactivate(), 982 fInitialName(name), 983 fLocation(location), 984 fInitialPriority(priority) 985 { 986 } 987 988 989 void 990 BPackageManager::InstalledRepository::GetPackagePath(BSolverPackage* package, 991 BPath& _path) 992 { 993 directory_which packagesWhich; 994 switch (fLocation) { 995 case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM: 996 packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY; 997 break; 998 case B_PACKAGE_INSTALLATION_LOCATION_HOME: 999 packagesWhich = B_USER_PACKAGES_DIRECTORY; 1000 break; 1001 default: 1002 DIE("Don't know packages directory path for installation location " 1003 "\"%s\"", Name().String()); 1004 } 1005 1006 BString fileName(package->Info().FileName()); 1007 status_t error = find_directory(packagesWhich, &_path); 1008 if (error != B_OK || (error = _path.Append(fileName)) != B_OK) { 1009 DIE(error, "Failed to get path of package file \"%s\" in installation " 1010 "location \"%s\"", fileName.String(), Name().String()); 1011 } 1012 } 1013 1014 1015 void 1016 BPackageManager::InstalledRepository::DisablePackage(BSolverPackage* package) 1017 { 1018 if (fDisabledPackages.HasItem(package)) 1019 DIE("Package %s already disabled", package->VersionedName().String()); 1020 1021 if (package->Repository() != this) { 1022 DIE("Package %s not in repository %s", 1023 package->VersionedName().String(), Name().String()); 1024 } 1025 1026 // move to disabled list 1027 if (!fDisabledPackages.AddItem(package)) 1028 throw std::bad_alloc(); 1029 1030 RemovePackage(package); 1031 } 1032 1033 1034 bool 1035 BPackageManager::InstalledRepository::EnablePackage(BSolverPackage* package) 1036 { 1037 return fDisabledPackages.RemoveItem(package); 1038 } 1039 1040 1041 bool 1042 BPackageManager::InstalledRepository::HasChanges() const 1043 { 1044 return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty(); 1045 } 1046 1047 1048 void 1049 BPackageManager::InstalledRepository::ApplyChanges() 1050 { 1051 // disable packages to deactivate 1052 for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i); 1053 i++) { 1054 if (!fDisabledPackages.HasItem(package)) 1055 DisablePackage(package); 1056 } 1057 1058 // add packages to activate 1059 for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i); 1060 i++) { 1061 status_t error = AddPackage(package->Info()); 1062 if (error != B_OK) { 1063 DIE(error, "Failed to add package %s to %s repository", 1064 package->Name().String(), Name().String()); 1065 } 1066 } 1067 } 1068 1069 1070 // #pragma mark - Transaction 1071 1072 1073 BPackageManager::Transaction::Transaction(InstalledRepository& repository) 1074 : 1075 fRepository(repository), 1076 fTransaction(), 1077 fTransactionDirectory() 1078 { 1079 } 1080 1081 1082 BPackageManager::Transaction::~Transaction() 1083 { 1084 } 1085 1086 1087 // #pragma mark - InstallationInterface 1088 1089 1090 BPackageManager::InstallationInterface::~InstallationInterface() 1091 { 1092 } 1093 1094 1095 void 1096 BPackageManager::InstallationInterface::ResultComputed( 1097 InstalledRepository& repository) 1098 { 1099 } 1100 1101 1102 // #pragma mark - ClientInstallationInterface 1103 1104 1105 BPackageManager::ClientInstallationInterface::ClientInstallationInterface() 1106 : 1107 fDaemonClient() 1108 { 1109 } 1110 1111 1112 BPackageManager::ClientInstallationInterface::~ClientInstallationInterface() 1113 { 1114 } 1115 1116 1117 void 1118 BPackageManager::ClientInstallationInterface::InitInstalledRepository( 1119 InstalledRepository& repository) 1120 { 1121 const char* name = repository.InitialName(); 1122 BRepositoryBuilder(repository, name) 1123 .AddPackages(repository.Location(), name); 1124 } 1125 1126 1127 status_t 1128 BPackageManager::ClientInstallationInterface::PrepareTransaction( 1129 Transaction& transaction) 1130 { 1131 return fDaemonClient.CreateTransaction(transaction.Repository().Location(), 1132 transaction.ActivationTransaction(), 1133 transaction.TransactionDirectory()); 1134 } 1135 1136 1137 status_t 1138 BPackageManager::ClientInstallationInterface::CommitTransaction( 1139 Transaction& transaction, BCommitTransactionResult& _result) 1140 { 1141 return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(), 1142 _result); 1143 } 1144 1145 1146 // #pragma mark - UserInteractionHandler 1147 1148 1149 BPackageManager::UserInteractionHandler::~UserInteractionHandler() 1150 { 1151 } 1152 1153 1154 void 1155 BPackageManager::UserInteractionHandler::HandleProblems() 1156 { 1157 throw BAbortedByUserException(); 1158 } 1159 1160 1161 void 1162 BPackageManager::UserInteractionHandler::ConfirmChanges(bool fromMostSpecific) 1163 { 1164 throw BAbortedByUserException(); 1165 } 1166 1167 1168 void 1169 BPackageManager::UserInteractionHandler::Warn(status_t error, 1170 const char* format, ...) 1171 { 1172 } 1173 1174 1175 void 1176 BPackageManager::UserInteractionHandler::ProgressPackageDownloadStarted( 1177 const char* packageName) 1178 { 1179 } 1180 1181 1182 void 1183 BPackageManager::UserInteractionHandler::ProgressPackageDownloadActive( 1184 const char* packageName, float completionPercentage, off_t bytes, 1185 off_t totalBytes) 1186 { 1187 } 1188 1189 1190 void 1191 BPackageManager::UserInteractionHandler::ProgressPackageDownloadComplete( 1192 const char* packageName) 1193 { 1194 } 1195 1196 1197 void 1198 BPackageManager::UserInteractionHandler::ProgressPackageChecksumStarted( 1199 const char* title) 1200 { 1201 } 1202 1203 1204 void 1205 BPackageManager::UserInteractionHandler::ProgressPackageChecksumComplete( 1206 const char* title) 1207 { 1208 } 1209 1210 1211 void 1212 BPackageManager::UserInteractionHandler::ProgressStartApplyingChanges( 1213 InstalledRepository& repository) 1214 { 1215 } 1216 1217 1218 void 1219 BPackageManager::UserInteractionHandler::ProgressTransactionCommitted( 1220 InstalledRepository& repository, const BCommitTransactionResult& result) 1221 { 1222 } 1223 1224 1225 void 1226 BPackageManager::UserInteractionHandler::ProgressApplyingChangesDone( 1227 InstalledRepository& repository) 1228 { 1229 } 1230 1231 1232 } // namespace BPrivate 1233 1234 } // namespace BManager 1235 1236 } // namespace BPackageKit 1237