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