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