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