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
41BSolver*
42BPackageKit::create_solver()
43{
44	return new(std::nothrow) LibsolvSolver;
45}
46
47
48struct LibsolvSolver::SolvQueue : Queue {
49	SolvQueue()
50	{
51		queue_init(this);
52	}
53
54	~SolvQueue()
55	{
56		queue_free(this);
57	}
58};
59
60
61struct 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
75struct 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
109private:
110	BSolverRepository*	fRepository;
111	Repo*				fSolvRepo;
112	uint64				fChangeCount;
113};
114
115
116struct 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
142private:
143	::Id			fId;
144	const Solution*	fSelectedSolution;
145};
146
147
148struct 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
167private:
168	::Id					fId;
169	LibsolvSolver::Problem*	fProblem;
170};
171
172
173// #pragma mark - LibsolvSolver
174
175
176LibsolvSolver::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
191LibsolvSolver::~LibsolvSolver()
192{
193	_Cleanup();
194}
195
196
197status_t
198LibsolvSolver::Init()
199{
200	_Cleanup();
201
202	// We do all initialization lazily.
203	return B_OK;
204}
205
206
207void
208LibsolvSolver::SetDebugLevel(int32 level)
209{
210	fDebugLevel = level;
211
212	if (fPool != NULL)
213		pool_setdebuglevel(fPool, fDebugLevel);
214}
215
216
217status_t
218LibsolvSolver::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
242status_t
243LibsolvSolver::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
308status_t
309LibsolvSolver::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
338status_t
339LibsolvSolver::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
370status_t
371LibsolvSolver::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
404status_t
405LibsolvSolver::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
449status_t
450LibsolvSolver::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
472status_t
473LibsolvSolver::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
500status_t
501LibsolvSolver::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
522status_t
523LibsolvSolver::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
540int32
541LibsolvSolver::CountProblems() const
542{
543	return fProblems.CountItems();
544}
545
546
547BSolverProblem*
548LibsolvSolver::ProblemAt(int32 index) const
549{
550	return fProblems.ItemAt(index);
551}
552
553
554status_t
555LibsolvSolver::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
601status_t
602LibsolvSolver::_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
638status_t
639LibsolvSolver::_InitJobQueue()
640{
641	_CleanupJobQueue();
642
643	fJobs = new(std::nothrow) SolvQueue;
644	return fJobs != NULL ? B_OK : B_NO_MEMORY;;
645}
646
647
648void
649LibsolvSolver::_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
659void
660LibsolvSolver::_Cleanup()
661{
662	_CleanupPool();
663
664	fInstalledRepository = NULL;
665	fRepositoryInfos.MakeEmpty();
666
667}
668
669
670void
671LibsolvSolver::_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
692void
693LibsolvSolver::_CleanupJobQueue()
694{
695	_CleanupSolver();
696
697	delete fJobs;
698	fJobs = NULL;
699}
700
701
702void
703LibsolvSolver::_CleanupSolver()
704{
705	fProblems.MakeEmpty();
706
707	if (fSolver != NULL) {
708		solver_free(fSolver);
709		fSolver = NULL;
710	}
711}
712
713
714bool
715LibsolvSolver::_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
728status_t
729LibsolvSolver::_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
782LibsolvSolver::RepositoryInfo*
783LibsolvSolver::_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
796LibsolvSolver::RepositoryInfo*
797LibsolvSolver::_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
810BSolverPackage*
811LibsolvSolver::_GetPackage(Id solvableId) const
812{
813	SolvableMap::const_iterator it = fSolvablePackages.find(solvableId);
814	return it != fSolvablePackages.end() ? it->second : NULL;
815}
816
817
818Id
819LibsolvSolver::_GetSolvable(BSolverPackage* package) const
820{
821	PackageMap::const_iterator it = fPackageSolvables.find(package);
822	return it != fPackageSolvables.end() ? it->second : 0;
823}
824
825
826status_t
827LibsolvSolver::_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
898status_t
899LibsolvSolver::_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
1029status_t
1030LibsolvSolver::_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
1056status_t
1057LibsolvSolver::_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
1185status_t
1186LibsolvSolver::_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
1220status_t
1221LibsolvSolver::_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
1285status_t
1286LibsolvSolver::_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
1314status_t
1315LibsolvSolver::_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
1335void
1336LibsolvSolver::_SetJobsSolverMode(int solverMode)
1337{
1338	for (int i = 0; i < fJobs->count; i += 2)
1339		fJobs->elements[i] |= solverMode;
1340}
1341