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*
create_solver()42 BPackageKit::create_solver()
43 {
44 return new(std::nothrow) LibsolvSolver;
45 }
46
47
48 struct LibsolvSolver::SolvQueue : Queue {
SolvQueueLibsolvSolver::SolvQueue49 SolvQueue()
50 {
51 queue_init(this);
52 }
53
~SolvQueueLibsolvSolver::SolvQueue54 ~SolvQueue()
55 {
56 queue_free(this);
57 }
58 };
59
60
61 struct LibsolvSolver::SolvDataIterator : Dataiterator {
SolvDataIteratorLibsolvSolver::SolvDataIterator62 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
~SolvDataIteratorLibsolvSolver::SolvDataIterator68 ~SolvDataIterator()
69 {
70 dataiterator_free(this);
71 }
72 };
73
74
75 struct LibsolvSolver::RepositoryInfo {
RepositoryInfoLibsolvSolver::RepositoryInfo76 RepositoryInfo(BSolverRepository* repository)
77 :
78 fRepository(repository),
79 fSolvRepo(NULL),
80 fChangeCount(repository->ChangeCount())
81 {
82 }
83
RepositoryLibsolvSolver::RepositoryInfo84 BSolverRepository* Repository() const
85 {
86 return fRepository;
87 }
88
SolvRepoLibsolvSolver::RepositoryInfo89 Repo* SolvRepo()
90 {
91 return fSolvRepo;
92 }
93
SetSolvRepoLibsolvSolver::RepositoryInfo94 void SetSolvRepo(Repo* repo)
95 {
96 fSolvRepo = repo;
97 }
98
HasChangedLibsolvSolver::RepositoryInfo99 bool HasChanged() const
100 {
101 return fChangeCount != fRepository->ChangeCount() || fSolvRepo == NULL;
102 }
103
SetUnchangedLibsolvSolver::RepositoryInfo104 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 {
ProblemLibsolvSolver::Problem117 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
IdLibsolvSolver::Problem127 ::Id Id() const
128 {
129 return fId;
130 }
131
SelectedSolutionLibsolvSolver::Problem132 const Solution* SelectedSolution() const
133 {
134 return fSelectedSolution;
135 }
136
SetSelectedSolutionLibsolvSolver::Problem137 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 {
SolutionLibsolvSolver::Solution149 Solution(::Id id, LibsolvSolver::Problem* problem)
150 :
151 BSolverProblemSolution(),
152 fId(id),
153 fProblem(problem)
154 {
155 }
156
IdLibsolvSolver::Solution157 ::Id Id() const
158 {
159 return fId;
160 }
161
ProblemLibsolvSolver::Solution162 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
LibsolvSolver()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
~LibsolvSolver()191 LibsolvSolver::~LibsolvSolver()
192 {
193 _Cleanup();
194 }
195
196
197 status_t
Init()198 LibsolvSolver::Init()
199 {
200 _Cleanup();
201
202 // We do all initialization lazily.
203 return B_OK;
204 }
205
206
207 void
SetDebugLevel(int32 level)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
AddRepository(BSolverRepository * repository)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
FindPackages(const char * searchString,uint32 flags,BObjectList<BSolverPackage> & _packages)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
FindPackages(const BSolverPackageSpecifierList & packages,uint32 flags,BObjectList<BSolverPackage> & _packages,const BSolverPackageSpecifier ** _unmatched)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
Install(const BSolverPackageSpecifierList & packages,const BSolverPackageSpecifier ** _unmatched)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
Uninstall(const BSolverPackageSpecifierList & packages,const BSolverPackageSpecifier ** _unmatched)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
Update(const BSolverPackageSpecifierList & packages,bool installNotYetInstalled,const BSolverPackageSpecifier ** _unmatched)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
FullSync()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
VerifyInstallation(uint32 flags)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
SelectProblemSolution(BSolverProblem * _problem,const BSolverProblemSolution * _solution)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
SolveAgain()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
CountProblems() const541 LibsolvSolver::CountProblems() const
542 {
543 return fProblems.CountItems();
544 }
545
546
547 BSolverProblem*
ProblemAt(int32 index) const548 LibsolvSolver::ProblemAt(int32 index) const
549 {
550 return fProblems.ItemAt(index);
551 }
552
553
554 status_t
GetResult(BSolverResult & _result)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, void, transaction_free>
564 transactionDeleter(transaction);
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
_InitPool()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
_InitJobQueue()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
_InitSolver()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
_Cleanup()660 LibsolvSolver::_Cleanup()
661 {
662 _CleanupPool();
663
664 fInstalledRepository = NULL;
665 fRepositoryInfos.MakeEmpty();
666
667 }
668
669
670 void
_CleanupPool()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
_CleanupJobQueue()693 LibsolvSolver::_CleanupJobQueue()
694 {
695 _CleanupSolver();
696
697 delete fJobs;
698 fJobs = NULL;
699 }
700
701
702 void
_CleanupSolver()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
_HaveRepositoriesChanged() const715 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
_AddRepositories()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*
_InstalledRepository() const783 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*
_GetRepositoryInfo(BSolverRepository * repository) const797 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*
_GetPackage(Id solvableId) const811 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
_GetSolvable(BSolverPackage * package) const819 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
_AddSpecifiedPackages(const BSolverPackageSpecifierList & packages,const BSolverPackageSpecifier ** _unmatched,int additionalFlags)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
_AddProblem(Id problemId)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
_AddSolution(Problem * problem,Id solutionId)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
_AddSolutionElement(Solution * solution,Id sourceId,Id targetId)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
_AddSolutionElement(Solution * solution,BSolverProblemSolutionElement::BType type,Id sourceSolvableId,Id targetSolvableId,const char * selectionString)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
_GetResolvableExpression(Id id,BPackageResolvableExpression & _expression) const1221 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
_GetFoundPackages(SolvQueue & selection,uint32 flags,BObjectList<BSolverPackage> & _packages)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
_Solve()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
_SetJobsSolverMode(int solverMode)1336 LibsolvSolver::_SetJobsSolverMode(int solverMode)
1337 {
1338 for (int i = 0; i < fJobs->count; i += 2)
1339 fJobs->elements[i] |= solverMode;
1340 }
1341