1 /* 2 * Copyright 2013, 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 */ 8 9 10 #include "LibsolvSolver.h" 11 12 #include <errno.h> 13 #include <sys/utsname.h> 14 15 #include <new> 16 17 #include <solv/policy.h> 18 #include <solv/poolarch.h> 19 #include <solv/repo.h> 20 #include <solv/repo_haiku.h> 21 #include <solv/selection.h> 22 #include <solv/solverdebug.h> 23 24 #include <package/PackageResolvableExpression.h> 25 #include <package/RepositoryCache.h> 26 #include <package/solver/SolverPackage.h> 27 #include <package/solver/SolverPackageSpecifier.h> 28 #include <package/solver/SolverPackageSpecifierList.h> 29 #include <package/solver/SolverProblem.h> 30 #include <package/solver/SolverRepository.h> 31 #include <package/solver/SolverResult.h> 32 33 #include <AutoDeleter.h> 34 #include <ObjectList.h> 35 36 37 // TODO: libsolv doesn't have any helpful out-of-memory handling. It just just 38 // abort()s. Obviously that isn't good behavior for a library. 39 40 41 BSolver* 42 BPackageKit::create_solver() 43 { 44 return new(std::nothrow) LibsolvSolver; 45 } 46 47 48 struct LibsolvSolver::SolvQueue : Queue { 49 SolvQueue() 50 { 51 queue_init(this); 52 } 53 54 ~SolvQueue() 55 { 56 queue_free(this); 57 } 58 }; 59 60 61 struct LibsolvSolver::SolvDataIterator : Dataiterator { 62 SolvDataIterator(Pool* pool, Repo* repo, Id solvableId, Id keyname, 63 const char* match, int flags) 64 { 65 dataiterator_init(this, pool, repo, solvableId, keyname, match, flags); 66 } 67 68 ~SolvDataIterator() 69 { 70 dataiterator_free(this); 71 } 72 }; 73 74 75 struct LibsolvSolver::RepositoryInfo { 76 RepositoryInfo(BSolverRepository* repository) 77 : 78 fRepository(repository), 79 fSolvRepo(NULL), 80 fChangeCount(repository->ChangeCount()) 81 { 82 } 83 84 BSolverRepository* Repository() const 85 { 86 return fRepository; 87 } 88 89 Repo* SolvRepo() 90 { 91 return fSolvRepo; 92 } 93 94 void SetSolvRepo(Repo* repo) 95 { 96 fSolvRepo = repo; 97 } 98 99 bool HasChanged() const 100 { 101 return fChangeCount != fRepository->ChangeCount() || fSolvRepo == NULL; 102 } 103 104 void SetUnchanged() 105 { 106 fChangeCount = fRepository->ChangeCount(); 107 } 108 109 private: 110 BSolverRepository* fRepository; 111 Repo* fSolvRepo; 112 uint64 fChangeCount; 113 }; 114 115 116 struct LibsolvSolver::Problem : public BSolverProblem { 117 Problem(::Id id, BType type, BSolverPackage* sourcePackage, 118 BSolverPackage* targetPackage, 119 const BPackageResolvableExpression& dependency) 120 : 121 BSolverProblem(type, sourcePackage, targetPackage, dependency), 122 fId(id), 123 fSelectedSolution(NULL) 124 { 125 } 126 127 ::Id Id() const 128 { 129 return fId; 130 } 131 132 const Solution* SelectedSolution() const 133 { 134 return fSelectedSolution; 135 } 136 137 void SetSelectedSolution(const Solution* solution) 138 { 139 fSelectedSolution = solution; 140 } 141 142 private: 143 ::Id fId; 144 const Solution* fSelectedSolution; 145 }; 146 147 148 struct LibsolvSolver::Solution : public BSolverProblemSolution { 149 Solution(::Id id, LibsolvSolver::Problem* problem) 150 : 151 BSolverProblemSolution(), 152 fId(id), 153 fProblem(problem) 154 { 155 } 156 157 ::Id Id() const 158 { 159 return fId; 160 } 161 162 LibsolvSolver::Problem* Problem() const 163 { 164 return fProblem; 165 } 166 167 private: 168 ::Id fId; 169 LibsolvSolver::Problem* fProblem; 170 }; 171 172 173 // #pragma mark - LibsolvSolver 174 175 176 LibsolvSolver::LibsolvSolver() 177 : 178 fPool(NULL), 179 fSolver(NULL), 180 fJobs(NULL), 181 fRepositoryInfos(10, true), 182 fInstalledRepository(NULL), 183 fSolvablePackages(), 184 fPackageSolvables(), 185 fProblems(10, true), 186 fDebugLevel(0) 187 { 188 } 189 190 191 LibsolvSolver::~LibsolvSolver() 192 { 193 _Cleanup(); 194 } 195 196 197 status_t 198 LibsolvSolver::Init() 199 { 200 _Cleanup(); 201 202 // We do all initialization lazily. 203 return B_OK; 204 } 205 206 207 void 208 LibsolvSolver::SetDebugLevel(int32 level) 209 { 210 fDebugLevel = level; 211 212 if (fPool != NULL) 213 pool_setdebuglevel(fPool, fDebugLevel); 214 } 215 216 217 status_t 218 LibsolvSolver::AddRepository(BSolverRepository* repository) 219 { 220 if (repository == NULL || repository->InitCheck() != B_OK) 221 return B_BAD_VALUE; 222 223 // If the repository represents installed packages, check, if we already 224 // have such a repository. 225 if (repository->IsInstalled() && _InstalledRepository() != NULL) 226 return B_BAD_VALUE; 227 228 // add the repository info 229 RepositoryInfo* info = new(std::nothrow) RepositoryInfo(repository); 230 if (info == NULL) 231 return B_NO_MEMORY; 232 233 if (!fRepositoryInfos.AddItem(info)) { 234 delete info; 235 return B_NO_MEMORY; 236 } 237 238 return B_OK; 239 } 240 241 242 status_t 243 LibsolvSolver::FindPackages(const char* searchString, uint32 flags, 244 BObjectList<BSolverPackage>& _packages) 245 { 246 // add repositories to pool 247 status_t error = _AddRepositories(); 248 if (error != B_OK) 249 return error; 250 251 // create data iterator 252 int iteratorFlags = SEARCH_SUBSTRING; 253 if ((flags & B_FIND_CASE_INSENSITIVE) != 0) 254 iteratorFlags |= SEARCH_NOCASE; 255 256 SolvDataIterator iterator(fPool, 0, 0, 0, searchString, iteratorFlags); 257 SolvQueue selection; 258 259 // search package names 260 if ((flags & B_FIND_IN_NAME) != 0) { 261 dataiterator_set_keyname(&iterator, SOLVABLE_NAME); 262 dataiterator_set_search(&iterator, 0, 0); 263 264 while (dataiterator_step(&iterator)) 265 queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid); 266 } 267 268 // search package summaries 269 if ((flags & B_FIND_IN_SUMMARY) != 0) { 270 dataiterator_set_keyname(&iterator, SOLVABLE_SUMMARY); 271 dataiterator_set_search(&iterator, 0, 0); 272 273 while (dataiterator_step(&iterator)) 274 queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid); 275 } 276 277 // search package description 278 if ((flags & B_FIND_IN_DESCRIPTION) != 0) { 279 dataiterator_set_keyname(&iterator, SOLVABLE_DESCRIPTION); 280 dataiterator_set_search(&iterator, 0, 0); 281 282 while (dataiterator_step(&iterator)) 283 queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid); 284 } 285 286 // search package provides 287 if ((flags & B_FIND_IN_PROVIDES) != 0) { 288 dataiterator_set_keyname(&iterator, SOLVABLE_PROVIDES); 289 dataiterator_set_search(&iterator, 0, 0); 290 291 while (dataiterator_step(&iterator)) 292 queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid); 293 } 294 295 // search package requires 296 if ((flags & B_FIND_IN_REQUIRES) != 0) { 297 dataiterator_set_keyname(&iterator, SOLVABLE_REQUIRES); 298 dataiterator_set_search(&iterator, 0, 0); 299 300 while (dataiterator_step(&iterator)) 301 queue_push2(&selection, SOLVER_SOLVABLE, iterator.solvid); 302 } 303 304 return _GetFoundPackages(selection, flags, _packages); 305 } 306 307 308 status_t 309 LibsolvSolver::FindPackages(const BSolverPackageSpecifierList& packages, 310 uint32 flags, BObjectList<BSolverPackage>& _packages, 311 const BSolverPackageSpecifier** _unmatched) 312 { 313 if (_unmatched != NULL) 314 *_unmatched = NULL; 315 316 if ((flags & B_FIND_INSTALLED_ONLY) != 0 && _InstalledRepository() == NULL) 317 return B_BAD_VALUE; 318 319 // add repositories to pool 320 status_t error = _AddRepositories(); 321 if (error != B_OK) 322 return error; 323 324 error = _InitJobQueue(); 325 if (error != B_OK) 326 return error; 327 328 // add the package specifies to the job queue 329 error = _AddSpecifiedPackages(packages, _unmatched, 330 (flags & B_FIND_INSTALLED_ONLY) != 0 ? SELECTION_INSTALLED_ONLY : 0); 331 if (error != B_OK) 332 return error; 333 334 return _GetFoundPackages(*fJobs, flags, _packages); 335 } 336 337 338 status_t 339 LibsolvSolver::Install(const BSolverPackageSpecifierList& packages, 340 const BSolverPackageSpecifier** _unmatched) 341 { 342 if (_unmatched != NULL) 343 *_unmatched = NULL; 344 345 if (packages.IsEmpty()) 346 return B_BAD_VALUE; 347 348 // add repositories to pool 349 status_t error = _AddRepositories(); 350 if (error != B_OK) 351 return error; 352 353 // add the packages to install to the job queue 354 error = _InitJobQueue(); 355 if (error != B_OK) 356 return error; 357 358 error = _AddSpecifiedPackages(packages, _unmatched, 0); 359 if (error != B_OK) 360 return error; 361 362 // set jobs' solver mode and solve 363 _SetJobsSolverMode(SOLVER_INSTALL); 364 365 _InitSolver(); 366 return _Solve(); 367 } 368 369 370 status_t 371 LibsolvSolver::Uninstall(const BSolverPackageSpecifierList& packages, 372 const BSolverPackageSpecifier** _unmatched) 373 { 374 if (_unmatched != NULL) 375 *_unmatched = NULL; 376 377 if (_InstalledRepository() == NULL || packages.IsEmpty()) 378 return B_BAD_VALUE; 379 380 // add repositories to pool 381 status_t error = _AddRepositories(); 382 if (error != B_OK) 383 return error; 384 385 // add the packages to uninstall to the job queue 386 error = _InitJobQueue(); 387 if (error != B_OK) 388 return error; 389 390 error = _AddSpecifiedPackages(packages, _unmatched, 391 SELECTION_INSTALLED_ONLY); 392 if (error != B_OK) 393 return error; 394 395 // set jobs' solver mode and solve 396 _SetJobsSolverMode(SOLVER_ERASE); 397 398 _InitSolver(); 399 solver_set_flag(fSolver, SOLVER_FLAG_ALLOW_UNINSTALL, 1); 400 return _Solve(); 401 } 402 403 404 status_t 405 LibsolvSolver::Update(const BSolverPackageSpecifierList& packages, 406 bool installNotYetInstalled, const BSolverPackageSpecifier** _unmatched) 407 { 408 if (_unmatched != NULL) 409 *_unmatched = NULL; 410 411 // add repositories to pool 412 status_t error = _AddRepositories(); 413 if (error != B_OK) 414 return error; 415 416 // add the packages to update to the job queue -- if none are specified, 417 // update all 418 error = _InitJobQueue(); 419 if (error != B_OK) 420 return error; 421 422 if (packages.IsEmpty()) { 423 queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0); 424 } else { 425 error = _AddSpecifiedPackages(packages, _unmatched, 0); 426 if (error != B_OK) 427 return error; 428 } 429 430 // set jobs' solver mode and solve 431 _SetJobsSolverMode(SOLVER_UPDATE); 432 433 if (installNotYetInstalled) { 434 for (int i = 0; i < fJobs->count; i += 2) { 435 // change solver mode to SOLVER_INSTALL for empty update jobs 436 if (pool_isemptyupdatejob(fPool, fJobs->elements[i], 437 fJobs->elements[i + 1])) { 438 fJobs->elements[i] &= ~SOLVER_JOBMASK; 439 fJobs->elements[i] |= SOLVER_INSTALL; 440 } 441 } 442 } 443 444 _InitSolver(); 445 return _Solve(); 446 } 447 448 449 status_t 450 LibsolvSolver::FullSync() 451 { 452 // add repositories to pool 453 status_t error = _AddRepositories(); 454 if (error != B_OK) 455 return error; 456 457 // Init the job queue and specify that all packages shall be updated. 458 error = _InitJobQueue(); 459 if (error != B_OK) 460 return error; 461 462 queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0); 463 464 // set jobs' solver mode and solve 465 _SetJobsSolverMode(SOLVER_DISTUPGRADE); 466 467 _InitSolver(); 468 return _Solve(); 469 } 470 471 472 status_t 473 LibsolvSolver::VerifyInstallation(uint32 flags) 474 { 475 if (_InstalledRepository() == NULL) 476 return B_BAD_VALUE; 477 478 // add repositories to pool 479 status_t error = _AddRepositories(); 480 if (error != B_OK) 481 return error; 482 483 // add the verify job to the job queue 484 error = _InitJobQueue(); 485 if (error != B_OK) 486 return error; 487 488 queue_push2(fJobs, SOLVER_SOLVABLE_ALL, 0); 489 490 // set jobs' solver mode and solve 491 _SetJobsSolverMode(SOLVER_VERIFY); 492 493 _InitSolver(); 494 if ((flags & B_VERIFY_ALLOW_UNINSTALL) != 0) 495 solver_set_flag(fSolver, SOLVER_FLAG_ALLOW_UNINSTALL, 1); 496 return _Solve(); 497 } 498 499 500 status_t 501 LibsolvSolver::SelectProblemSolution(BSolverProblem* _problem, 502 const BSolverProblemSolution* _solution) 503 { 504 if (_problem == NULL) 505 return B_BAD_VALUE; 506 507 Problem* problem = static_cast<Problem*>(_problem); 508 if (_solution == NULL) { 509 problem->SetSelectedSolution(NULL); 510 return B_OK; 511 } 512 513 const Solution* solution = static_cast<const Solution*>(_solution); 514 if (solution->Problem() != problem) 515 return B_BAD_VALUE; 516 517 problem->SetSelectedSolution(solution); 518 return B_OK; 519 } 520 521 522 status_t 523 LibsolvSolver::SolveAgain() 524 { 525 if (fSolver == NULL || fJobs == NULL) 526 return B_BAD_VALUE; 527 528 // iterate through all problems and propagate the selected solutions 529 int32 problemCount = fProblems.CountItems(); 530 for (int32 i = 0; i < problemCount; i++) { 531 Problem* problem = fProblems.ItemAt(i); 532 if (const Solution* solution = problem->SelectedSolution()) 533 solver_take_solution(fSolver, problem->Id(), solution->Id(), fJobs); 534 } 535 536 return _Solve(); 537 } 538 539 540 int32 541 LibsolvSolver::CountProblems() const 542 { 543 return fProblems.CountItems(); 544 } 545 546 547 BSolverProblem* 548 LibsolvSolver::ProblemAt(int32 index) const 549 { 550 return fProblems.ItemAt(index); 551 } 552 553 554 status_t 555 LibsolvSolver::GetResult(BSolverResult& _result) 556 { 557 if (fSolver == NULL || HasProblems()) 558 return B_BAD_VALUE; 559 560 _result.MakeEmpty(); 561 562 Transaction* transaction = solver_create_transaction(fSolver); 563 CObjectDeleter<Transaction> transactionDeleter(transaction, 564 &transaction_free); 565 566 if (transaction->steps.count == 0) 567 return B_OK; 568 569 transaction_order(transaction, 0); 570 571 for (int i = 0; i < transaction->steps.count; i++) { 572 Id solvableId = transaction->steps.elements[i]; 573 if (fPool->installed 574 && fPool->solvables[solvableId].repo == fPool->installed) { 575 BSolverPackage* package = _GetPackage(solvableId); 576 if (package == NULL) 577 return B_ERROR; 578 579 if (!_result.AppendElement( 580 BSolverResultElement( 581 BSolverResultElement::B_TYPE_UNINSTALL, package))) { 582 return B_NO_MEMORY; 583 } 584 } else { 585 BSolverPackage* package = _GetPackage(solvableId); 586 if (package == NULL) 587 return B_ERROR; 588 589 if (!_result.AppendElement( 590 BSolverResultElement( 591 BSolverResultElement::B_TYPE_INSTALL, package))) { 592 return B_NO_MEMORY; 593 } 594 } 595 } 596 597 return B_OK; 598 } 599 600 601 status_t 602 LibsolvSolver::_InitPool() 603 { 604 _CleanupPool(); 605 606 fPool = pool_create(); 607 608 pool_setdebuglevel(fPool, fDebugLevel); 609 610 // Set the system architecture. We use what uname() returns unless we're on 611 // x86 gcc2. 612 { 613 const char* arch; 614 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 615 #ifdef __HAIKU_ARCH_X86 616 #if (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) == B_HAIKU_ABI_GCC_2 617 arch = "x86_gcc2"; 618 #else 619 arch = "x86"; 620 #endif 621 #else 622 struct utsname info; 623 if (uname(&info) != 0) 624 return errno; 625 arch = info.machine; 626 #endif 627 #else 628 arch = HAIKU_PACKAGING_ARCH; 629 #endif 630 631 pool_setarchpolicy(fPool, arch); 632 } 633 634 return B_OK; 635 } 636 637 638 status_t 639 LibsolvSolver::_InitJobQueue() 640 { 641 _CleanupJobQueue(); 642 643 fJobs = new(std::nothrow) SolvQueue; 644 return fJobs != NULL ? B_OK : B_NO_MEMORY;; 645 } 646 647 648 void 649 LibsolvSolver::_InitSolver() 650 { 651 _CleanupSolver(); 652 653 fSolver = solver_create(fPool); 654 solver_set_flag(fSolver, SOLVER_FLAG_SPLITPROVIDES, 1); 655 solver_set_flag(fSolver, SOLVER_FLAG_BEST_OBEY_POLICY, 1); 656 } 657 658 659 void 660 LibsolvSolver::_Cleanup() 661 { 662 _CleanupPool(); 663 664 fInstalledRepository = NULL; 665 fRepositoryInfos.MakeEmpty(); 666 667 } 668 669 670 void 671 LibsolvSolver::_CleanupPool() 672 { 673 // clean up jobs and solver data 674 _CleanupJobQueue(); 675 676 // clean up our data structures that depend on/refer to libsolv pool data 677 fSolvablePackages.clear(); 678 fPackageSolvables.clear(); 679 680 int32 repositoryCount = fRepositoryInfos.CountItems(); 681 for (int32 i = 0; i < repositoryCount; i++) 682 fRepositoryInfos.ItemAt(i)->SetSolvRepo(NULL); 683 684 // delete the pool 685 if (fPool != NULL) { 686 pool_free(fPool); 687 fPool = NULL; 688 } 689 } 690 691 692 void 693 LibsolvSolver::_CleanupJobQueue() 694 { 695 _CleanupSolver(); 696 697 delete fJobs; 698 fJobs = NULL; 699 } 700 701 702 void 703 LibsolvSolver::_CleanupSolver() 704 { 705 fProblems.MakeEmpty(); 706 707 if (fSolver != NULL) { 708 solver_free(fSolver); 709 fSolver = NULL; 710 } 711 } 712 713 714 bool 715 LibsolvSolver::_HaveRepositoriesChanged() const 716 { 717 int32 repositoryCount = fRepositoryInfos.CountItems(); 718 for (int32 i = 0; i < repositoryCount; i++) { 719 RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i); 720 if (repositoryInfo->HasChanged()) 721 return true; 722 } 723 724 return false; 725 } 726 727 728 status_t 729 LibsolvSolver::_AddRepositories() 730 { 731 if (fPool != NULL && !_HaveRepositoriesChanged()) 732 return B_OK; 733 734 // something has changed -- re-create the pool 735 status_t error = _InitPool(); 736 if (error != B_OK) 737 return error; 738 739 fInstalledRepository = NULL; 740 741 int32 repositoryCount = fRepositoryInfos.CountItems(); 742 for (int32 i = 0; i < repositoryCount; i++) { 743 RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i); 744 BSolverRepository* repository = repositoryInfo->Repository(); 745 Repo* repo = repo_create(fPool, repository->Name()); 746 repositoryInfo->SetSolvRepo(repo); 747 748 repo->priority = -1 - repository->Priority(); 749 repo->appdata = (void*)repositoryInfo; 750 751 int32 packageCount = repository->CountPackages(); 752 for (int32 k = 0; k < packageCount; k++) { 753 BSolverPackage* package = repository->PackageAt(k); 754 Id solvableId = repo_add_haiku_package_info(repo, package->Info(), 755 REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE); 756 757 try { 758 fSolvablePackages[solvableId] = package; 759 fPackageSolvables[package] = solvableId; 760 } catch (std::bad_alloc&) { 761 return B_NO_MEMORY; 762 } 763 } 764 765 repo_internalize(repo); 766 767 if (repository->IsInstalled()) { 768 fInstalledRepository = repositoryInfo; 769 pool_set_installed(fPool, repo); 770 } 771 772 repositoryInfo->SetUnchanged(); 773 } 774 775 // create "provides" lookup 776 pool_createwhatprovides(fPool); 777 778 return B_OK; 779 } 780 781 782 LibsolvSolver::RepositoryInfo* 783 LibsolvSolver::_InstalledRepository() const 784 { 785 int32 repositoryCount = fRepositoryInfos.CountItems(); 786 for (int32 i = 0; i < repositoryCount; i++) { 787 RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i); 788 if (repositoryInfo->Repository()->IsInstalled()) 789 return repositoryInfo; 790 } 791 792 return NULL; 793 } 794 795 796 LibsolvSolver::RepositoryInfo* 797 LibsolvSolver::_GetRepositoryInfo(BSolverRepository* repository) const 798 { 799 int32 repositoryCount = fRepositoryInfos.CountItems(); 800 for (int32 i = 0; i < repositoryCount; i++) { 801 RepositoryInfo* repositoryInfo = fRepositoryInfos.ItemAt(i); 802 if (repository == repositoryInfo->Repository()) 803 return repositoryInfo; 804 } 805 806 return NULL; 807 } 808 809 810 BSolverPackage* 811 LibsolvSolver::_GetPackage(Id solvableId) const 812 { 813 SolvableMap::const_iterator it = fSolvablePackages.find(solvableId); 814 return it != fSolvablePackages.end() ? it->second : NULL; 815 } 816 817 818 Id 819 LibsolvSolver::_GetSolvable(BSolverPackage* package) const 820 { 821 PackageMap::const_iterator it = fPackageSolvables.find(package); 822 return it != fPackageSolvables.end() ? it->second : 0; 823 } 824 825 826 status_t 827 LibsolvSolver::_AddSpecifiedPackages( 828 const BSolverPackageSpecifierList& packages, 829 const BSolverPackageSpecifier** _unmatched, int additionalFlags) 830 { 831 int32 packageCount = packages.CountSpecifiers(); 832 for (int32 i = 0; i < packageCount; i++) { 833 const BSolverPackageSpecifier& specifier = *packages.SpecifierAt(i); 834 switch (specifier.Type()) { 835 case BSolverPackageSpecifier::B_UNSPECIFIED: 836 return B_BAD_VALUE; 837 838 case BSolverPackageSpecifier::B_PACKAGE: 839 { 840 BSolverPackage* package = specifier.Package(); 841 Id solvableId; 842 if (package == NULL 843 || (solvableId = _GetSolvable(package)) == 0) { 844 return B_BAD_VALUE; 845 } 846 847 queue_push2(fJobs, SOLVER_SOLVABLE, solvableId); 848 break; 849 } 850 851 case BSolverPackageSpecifier::B_SELECT_STRING: 852 { 853 // find matching packages 854 SolvQueue matchingPackages; 855 856 int flags = SELECTION_NAME | SELECTION_PROVIDES | SELECTION_GLOB 857 | SELECTION_CANON | SELECTION_DOTARCH | SELECTION_REL 858 | additionalFlags; 859 /*int matchFlags =*/ selection_make(fPool, &matchingPackages, 860 specifier.SelectString().String(), flags); 861 if (matchingPackages.count == 0) { 862 if (_unmatched != NULL) 863 *_unmatched = &specifier; 864 return B_NAME_NOT_FOUND; 865 } 866 // TODO: We might want to add support for restricting to certain repositories. 867 #if 0 868 // restrict to the matching repository 869 if (BSolverRepository* repository = specifier.Repository()) { 870 RepositoryInfo* repositoryInfo 871 = _GetRepositoryInfo(repository); 872 if (repositoryInfo == NULL) 873 return B_BAD_VALUE; 874 875 SolvQueue repoFilter; 876 queue_push2(&repoFilter, 877 SOLVER_SOLVABLE_REPO 878 /* | SOLVER_SETREPO | SOLVER_SETVENDOR*/, 879 repositoryInfo->SolvRepo()->repoid); 880 881 selection_filter(fPool, &matchingPackages, &repoFilter); 882 883 if (matchingPackages.count == 0) 884 return B_NAME_NOT_FOUND; 885 } 886 #endif 887 888 for (int j = 0; j < matchingPackages.count; j++) 889 queue_push(fJobs, matchingPackages.elements[j]); 890 } 891 } 892 } 893 894 return B_OK; 895 } 896 897 898 status_t 899 LibsolvSolver::_AddProblem(Id problemId) 900 { 901 enum { 902 NEED_SOURCE = 0x1, 903 NEED_TARGET = 0x2, 904 NEED_DEPENDENCY = 0x4 905 }; 906 907 Id ruleId = solver_findproblemrule(fSolver, problemId); 908 Id sourceId; 909 Id targetId; 910 Id dependencyId; 911 BSolverProblem::BType problemType = BSolverProblem::B_UNSPECIFIED; 912 uint32 needed = 0; 913 914 switch (solver_ruleinfo(fSolver, ruleId, &sourceId, &targetId, 915 &dependencyId)) { 916 case SOLVER_RULE_DISTUPGRADE: 917 problemType = BSolverProblem::B_NOT_IN_DISTUPGRADE_REPOSITORY; 918 needed = NEED_SOURCE; 919 break; 920 case SOLVER_RULE_INFARCH: 921 problemType = BSolverProblem::B_INFERIOR_ARCHITECTURE; 922 needed = NEED_SOURCE; 923 break; 924 case SOLVER_RULE_UPDATE: 925 problemType = BSolverProblem::B_INSTALLED_PACKAGE_PROBLEM; 926 needed = NEED_SOURCE; 927 break; 928 case SOLVER_RULE_JOB: 929 problemType = BSolverProblem::B_CONFLICTING_REQUESTS; 930 break; 931 case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP: 932 problemType = BSolverProblem::B_REQUESTED_RESOLVABLE_NOT_PROVIDED; 933 needed = NEED_DEPENDENCY; 934 break; 935 case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM: 936 problemType 937 = BSolverProblem::B_REQUESTED_RESOLVABLE_PROVIDED_BY_SYSTEM; 938 needed = NEED_DEPENDENCY; 939 break; 940 case SOLVER_RULE_RPM: 941 problemType = BSolverProblem::B_DEPENDENCY_PROBLEM; 942 break; 943 case SOLVER_RULE_RPM_NOT_INSTALLABLE: 944 problemType = BSolverProblem::B_PACKAGE_NOT_INSTALLABLE; 945 needed = NEED_SOURCE; 946 break; 947 case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP: 948 problemType = BSolverProblem::B_DEPENDENCY_NOT_PROVIDED; 949 needed = NEED_SOURCE | NEED_DEPENDENCY; 950 break; 951 case SOLVER_RULE_RPM_SAME_NAME: 952 problemType = BSolverProblem::B_PACKAGE_NAME_CLASH; 953 needed = NEED_SOURCE | NEED_TARGET; 954 break; 955 case SOLVER_RULE_RPM_PACKAGE_CONFLICT: 956 problemType = BSolverProblem::B_PACKAGE_CONFLICT; 957 needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY; 958 break; 959 case SOLVER_RULE_RPM_PACKAGE_OBSOLETES: 960 problemType = BSolverProblem::B_PACKAGE_OBSOLETES_RESOLVABLE; 961 needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY; 962 break; 963 case SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES: 964 problemType 965 = BSolverProblem::B_INSTALLED_PACKAGE_OBSOLETES_RESOLVABLE; 966 needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY; 967 break; 968 case SOLVER_RULE_RPM_IMPLICIT_OBSOLETES: 969 problemType 970 = BSolverProblem::B_PACKAGE_IMPLICITLY_OBSOLETES_RESOLVABLE; 971 needed = NEED_SOURCE | NEED_TARGET | NEED_DEPENDENCY; 972 break; 973 case SOLVER_RULE_RPM_PACKAGE_REQUIRES: 974 problemType = BSolverProblem::B_DEPENDENCY_NOT_INSTALLABLE; 975 needed = NEED_SOURCE | NEED_DEPENDENCY; 976 break; 977 case SOLVER_RULE_RPM_SELF_CONFLICT: 978 problemType = BSolverProblem::B_SELF_CONFLICT; 979 needed = NEED_SOURCE | NEED_DEPENDENCY; 980 break; 981 case SOLVER_RULE_UNKNOWN: 982 case SOLVER_RULE_FEATURE: 983 case SOLVER_RULE_LEARNT: 984 case SOLVER_RULE_CHOICE: 985 case SOLVER_RULE_BEST: 986 problemType = BSolverProblem::B_UNSPECIFIED; 987 break; 988 } 989 990 BSolverPackage* sourcePackage = NULL; 991 if ((needed & NEED_SOURCE) != 0) { 992 sourcePackage = _GetPackage(sourceId); 993 if (sourcePackage == NULL) 994 return B_ERROR; 995 } 996 997 BSolverPackage* targetPackage = NULL; 998 if ((needed & NEED_TARGET) != 0) { 999 targetPackage = _GetPackage(targetId); 1000 if (targetPackage == NULL) 1001 return B_ERROR; 1002 } 1003 1004 BPackageResolvableExpression dependency; 1005 if ((needed & NEED_DEPENDENCY) != 0) { 1006 status_t error = _GetResolvableExpression(dependencyId, dependency); 1007 if (error != B_OK) 1008 return error; 1009 } 1010 1011 Problem* problem = new(std::nothrow) Problem(problemId, problemType, 1012 sourcePackage, targetPackage, dependency); 1013 if (problem == NULL || !fProblems.AddItem(problem)) { 1014 delete problem; 1015 return B_NO_MEMORY; 1016 } 1017 1018 int solutionCount = solver_solution_count(fSolver, problemId); 1019 for (Id solutionId = 1; solutionId <= solutionCount; solutionId++) { 1020 status_t error = _AddSolution(problem, solutionId); 1021 if (error != B_OK) 1022 return error; 1023 } 1024 1025 return B_OK; 1026 } 1027 1028 1029 status_t 1030 LibsolvSolver::_AddSolution(Problem* problem, Id solutionId) 1031 { 1032 Solution* solution = new(std::nothrow) Solution(solutionId, problem); 1033 if (solution == NULL || !problem->AppendSolution(solution)) { 1034 delete solution; 1035 return B_NO_MEMORY; 1036 } 1037 1038 Id elementId = 0; 1039 for (;;) { 1040 Id sourceId; 1041 Id targetId; 1042 elementId = solver_next_solutionelement(fSolver, problem->Id(), 1043 solutionId, elementId, &sourceId, &targetId); 1044 if (elementId == 0) 1045 break; 1046 1047 status_t error = _AddSolutionElement(solution, sourceId, targetId); 1048 if (error != B_OK) 1049 return error; 1050 } 1051 1052 return B_OK; 1053 } 1054 1055 1056 status_t 1057 LibsolvSolver::_AddSolutionElement(Solution* solution, Id sourceId, Id targetId) 1058 { 1059 typedef BSolverProblemSolutionElement Element; 1060 1061 if (sourceId == SOLVER_SOLUTION_JOB 1062 || sourceId == SOLVER_SOLUTION_POOLJOB) { 1063 // targetId is an index into the job queue 1064 if (sourceId == SOLVER_SOLUTION_JOB) 1065 targetId += fSolver->pooljobcnt; 1066 1067 Id how = fSolver->job.elements[targetId - 1]; 1068 Id what = fSolver->job.elements[targetId]; 1069 Id select = how & SOLVER_SELECTMASK; 1070 1071 switch (how & SOLVER_JOBMASK) { 1072 case SOLVER_INSTALL: 1073 if (select == SOLVER_SOLVABLE && fInstalledRepository != NULL 1074 && fPool->solvables[what].repo 1075 == fInstalledRepository->SolvRepo()) { 1076 return _AddSolutionElement(solution, Element::B_DONT_KEEP, 1077 what, 0, NULL); 1078 } 1079 1080 return _AddSolutionElement(solution, 1081 Element::B_DONT_INSTALL, 0, 0, 1082 solver_select2str(fPool, select, what)); 1083 1084 case SOLVER_ERASE: 1085 { 1086 if (select == SOLVER_SOLVABLE 1087 && (fInstalledRepository == NULL 1088 || fPool->solvables[what].repo 1089 != fInstalledRepository->SolvRepo())) { 1090 return _AddSolutionElement(solution, 1091 Element::B_DONT_FORBID_INSTALLATION, what, 0, NULL); 1092 } 1093 1094 Element::BType type = select == SOLVER_SOLVABLE_PROVIDES 1095 ? Element::B_DONT_DEINSTALL_ALL : Element::B_DONT_DEINSTALL; 1096 return _AddSolutionElement(solution, type, 0, 0, 1097 solver_select2str(fPool, select, what)); 1098 } 1099 1100 case SOLVER_UPDATE: 1101 return _AddSolutionElement(solution, 1102 Element::B_DONT_INSTALL_MOST_RECENT, 0, 0, 1103 solver_select2str(fPool, select, what)); 1104 1105 case SOLVER_LOCK: 1106 return _AddSolutionElement(solution, Element::B_DONT_LOCK, 0, 0, 1107 solver_select2str(fPool, select, what)); 1108 1109 default: 1110 return _AddSolutionElement(solution, Element::B_UNSPECIFIED, 0, 1111 0, NULL); 1112 } 1113 } 1114 1115 Solvable* target = targetId != 0 ? fPool->solvables + targetId : NULL; 1116 bool targetInstalled = target != NULL && fInstalledRepository 1117 && target->repo == fInstalledRepository->SolvRepo(); 1118 1119 if (sourceId == SOLVER_SOLUTION_INFARCH) { 1120 return _AddSolutionElement(solution, 1121 targetInstalled 1122 ? Element::B_KEEP_INFERIOR_ARCHITECTURE 1123 : Element::B_INSTALL_INFERIOR_ARCHITECTURE, 1124 targetId, 0, NULL); 1125 } 1126 1127 if (sourceId == SOLVER_SOLUTION_DISTUPGRADE) { 1128 return _AddSolutionElement(solution, 1129 targetInstalled 1130 ? Element::B_KEEP_EXCLUDED : Element::B_INSTALL_EXCLUDED, 1131 targetId, 0, NULL); 1132 } 1133 1134 if (sourceId == SOLVER_SOLUTION_BEST) { 1135 return _AddSolutionElement(solution, 1136 targetInstalled ? Element::B_KEEP_OLD : Element::B_INSTALL_OLD, 1137 targetId, 0, NULL); 1138 } 1139 1140 // replace source with target 1141 Solvable* source = fPool->solvables + sourceId; 1142 if (target == NULL) { 1143 return _AddSolutionElement(solution, Element::B_ALLOW_DEINSTALLATION, 1144 sourceId, 0, NULL); 1145 } 1146 1147 int illegalMask = policy_is_illegal(fSolver, source, target, 0); 1148 if ((illegalMask & POLICY_ILLEGAL_DOWNGRADE) != 0) { 1149 status_t error = _AddSolutionElement(solution, 1150 Element::B_ALLOW_DOWNGRADE, sourceId, targetId, NULL); 1151 if (error != B_OK) 1152 return error; 1153 } 1154 1155 if ((illegalMask & POLICY_ILLEGAL_NAMECHANGE) != 0) { 1156 status_t error = _AddSolutionElement(solution, 1157 Element::B_ALLOW_NAME_CHANGE, sourceId, targetId, NULL); 1158 if (error != B_OK) 1159 return error; 1160 } 1161 1162 if ((illegalMask & POLICY_ILLEGAL_ARCHCHANGE) != 0) { 1163 status_t error = _AddSolutionElement(solution, 1164 Element::B_ALLOW_ARCHITECTURE_CHANGE, sourceId, targetId, NULL); 1165 if (error != B_OK) 1166 return error; 1167 } 1168 1169 if ((illegalMask & POLICY_ILLEGAL_VENDORCHANGE) != 0) { 1170 status_t error = _AddSolutionElement(solution, 1171 Element::B_ALLOW_VENDOR_CHANGE, sourceId, targetId, NULL); 1172 if (error != B_OK) 1173 return error; 1174 } 1175 1176 if (illegalMask == 0) { 1177 return _AddSolutionElement(solution, Element::B_ALLOW_REPLACEMENT, 1178 sourceId, targetId, NULL); 1179 } 1180 1181 return B_OK; 1182 } 1183 1184 1185 status_t 1186 LibsolvSolver::_AddSolutionElement(Solution* solution, 1187 BSolverProblemSolutionElement::BType type, Id sourceSolvableId, 1188 Id targetSolvableId, const char* selectionString) 1189 { 1190 BSolverPackage* sourcePackage = NULL; 1191 if (sourceSolvableId != 0) { 1192 sourcePackage = _GetPackage(sourceSolvableId); 1193 if (sourcePackage == NULL) 1194 return B_ERROR; 1195 } 1196 1197 BSolverPackage* targetPackage = NULL; 1198 if (targetSolvableId != 0) { 1199 targetPackage = _GetPackage(targetSolvableId); 1200 if (targetPackage == NULL) 1201 return B_ERROR; 1202 } 1203 1204 BString selection; 1205 if (selectionString != NULL && selectionString[0] != '\0') { 1206 selection = selectionString; 1207 if (selection.IsEmpty()) 1208 return B_NO_MEMORY; 1209 } 1210 1211 if (!solution->AppendElement(BSolverProblemSolutionElement( 1212 type, sourcePackage, targetPackage, selection))) { 1213 return B_NO_MEMORY; 1214 } 1215 1216 return B_OK; 1217 } 1218 1219 1220 status_t 1221 LibsolvSolver::_GetResolvableExpression(Id id, 1222 BPackageResolvableExpression& _expression) const 1223 { 1224 // Try to translate the libsolv ID to a resolvable expression. Generally 1225 // that doesn't work, since libsolv is more expressive, but all the stuff 1226 // we feed libsolv we should be able to translate back. 1227 1228 if (!ISRELDEP(id)) { 1229 // just a string 1230 _expression.SetTo(pool_id2str(fPool, id)); 1231 return B_OK; 1232 } 1233 1234 // a composite -- analyze it 1235 Reldep* reldep = GETRELDEP(fPool, id); 1236 1237 // No support for more than one level, so both name and evr must be strings. 1238 if (ISRELDEP(reldep->name) || ISRELDEP(reldep->evr)) 1239 return B_NOT_SUPPORTED; 1240 1241 const char* name = pool_id2str(fPool, reldep->name); 1242 const char* versionString = pool_id2str(fPool, reldep->evr); 1243 if (name == NULL || versionString == NULL) 1244 return B_NOT_SUPPORTED; 1245 1246 // get the operator -- we don't support all libsolv supports 1247 BPackageResolvableOperator op; 1248 switch (reldep->flags) { 1249 case 1: 1250 op = B_PACKAGE_RESOLVABLE_OP_GREATER; 1251 break; 1252 case 2: 1253 op = B_PACKAGE_RESOLVABLE_OP_EQUAL; 1254 break; 1255 case 3: 1256 op = B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL; 1257 break; 1258 case 4: 1259 op = B_PACKAGE_RESOLVABLE_OP_LESS; 1260 break; 1261 case 5: 1262 op = B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL; 1263 break; 1264 case 6: 1265 op = B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL; 1266 break; 1267 default: 1268 return B_NOT_SUPPORTED; 1269 } 1270 1271 // get the version (cut off the empty epoch) 1272 if (versionString[0] == ':') 1273 versionString++; 1274 1275 BPackageVersion version; 1276 status_t error = version.SetTo(versionString, true); 1277 if (error != B_OK) 1278 return error == B_BAD_DATA ? B_NOT_SUPPORTED : error; 1279 1280 _expression.SetTo(name, op, version); 1281 return B_OK; 1282 } 1283 1284 1285 status_t 1286 LibsolvSolver::_GetFoundPackages(SolvQueue& selection, uint32 flags, 1287 BObjectList<BSolverPackage>& _packages) 1288 { 1289 // get solvables 1290 SolvQueue solvables; 1291 selection_solvables(fPool, &selection, &solvables); 1292 1293 // get packages 1294 for (int i = 0; i < solvables.count; i++) { 1295 BSolverPackage* package = _GetPackage(solvables.elements[i]); 1296 if (package == NULL) 1297 return B_ERROR; 1298 1299 // TODO: Fix handling of SELECTION_INSTALLED_ONLY in libsolv. Despite 1300 // passing the flag, we get solvables that aren't installed. 1301 if ((flags & B_FIND_INSTALLED_ONLY) != 0 1302 && package->Repository() != fInstalledRepository->Repository()) { 1303 continue; 1304 } 1305 1306 if (!_packages.AddItem(package)) 1307 return B_NO_MEMORY; 1308 } 1309 1310 return B_OK; 1311 } 1312 1313 1314 status_t 1315 LibsolvSolver::_Solve() 1316 { 1317 if (fJobs == NULL || fSolver == NULL) 1318 return B_BAD_VALUE; 1319 1320 int problemCount = solver_solve(fSolver, fJobs); 1321 1322 // get the problems (if any) 1323 fProblems.MakeEmpty(); 1324 1325 for (Id problemId = 1; problemId <= problemCount; problemId++) { 1326 status_t error = _AddProblem(problemId); 1327 if (error != B_OK) 1328 return error; 1329 } 1330 1331 return B_OK; 1332 } 1333 1334 1335 void 1336 LibsolvSolver::_SetJobsSolverMode(int solverMode) 1337 { 1338 for (int i = 0; i < fJobs->count; i += 2) 1339 fJobs->elements[i] |= solverMode; 1340 } 1341