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