xref: /haiku/src/kits/package/manager/PackageManager.cpp (revision b65adbdfbc322bb7d86d74049389c688e9962f15)
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");
567 		} else if (package->Repository() != &installationRepository) {
568 			// clone the existing package
569 			LocalRepository* localRepository
570 				= dynamic_cast<LocalRepository*>(package->Repository());
571 			if (localRepository == NULL) {
572 				DIE("internal error: repository %s is not a local repository",
573 					package->Repository()->Name().String());
574 			}
575 			_ClonePackageFile(localRepository, package, entry);
576 		}
577 
578 		// add package to transaction
579 		if (!transaction->ActivationTransaction().AddPackageToActivate(
580 				fileName)) {
581 			throw std::bad_alloc();
582 		}
583 	}
584 
585 	for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
586 		i++) {
587 		// add package to transaction
588 		if (!transaction->ActivationTransaction().AddPackageToDeactivate(
589 				package->Info().FileName())) {
590 			throw std::bad_alloc();
591 		}
592 	}
593 }
594 
595 
596 void
597 BPackageManager::_CommitPackageChanges(Transaction& transaction)
598 {
599 	InstalledRepository& installationRepository = transaction.Repository();
600 
601 	fUserInteractionHandler->ProgressStartApplyingChanges(
602 		installationRepository);
603 
604 	// commit the transaction
605 	BCommitTransactionResult transactionResult;
606 	status_t error = fInstallationInterface->CommitTransaction(transaction,
607 		transactionResult);
608 	if (error != B_OK)
609 		DIE(error, "failed to commit transaction");
610 	if (transactionResult.Error() != B_TRANSACTION_OK)
611 		DIE(transactionResult);
612 
613 	fUserInteractionHandler->ProgressTransactionCommitted(
614 		installationRepository, transactionResult);
615 
616 	BEntry transactionDirectoryEntry;
617 	if ((error = transaction.TransactionDirectory()
618 			.GetEntry(&transactionDirectoryEntry)) != B_OK
619 		|| (error = transactionDirectoryEntry.Remove()) != B_OK) {
620 		fUserInteractionHandler->Warn(error,
621 			"failed to remove transaction directory");
622 	}
623 
624 	fUserInteractionHandler->ProgressApplyingChangesDone(
625 		installationRepository);
626 }
627 
628 
629 void
630 BPackageManager::_ClonePackageFile(LocalRepository* repository,
631 	BSolverPackage* package, const BEntry& entry)
632 {
633 	// get source and destination path
634 	BPath sourcePath;
635 	repository->GetPackagePath(package, sourcePath);
636 
637 	BPath destinationPath;
638 	status_t error = entry.GetPath(&destinationPath);
639 	if (error != B_OK) {
640 		DIE(error, "failed to entry path of package file to install \"%s\"",
641 			package->Info().FileName().String());
642 	}
643 
644 	// Copy the package. Ideally we would just hard-link it, but BFS doesn't
645 	// support that.
646 	error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path());
647 	if (error != B_OK)
648 		DIE(error, "failed to copy package file \"%s\"", sourcePath.Path());
649 }
650 
651 
652 int32
653 BPackageManager::_FindBasePackage(const PackageList& packages,
654 	const BPackageInfo& info)
655 {
656 	if (info.BasePackage().IsEmpty())
657 		return -1;
658 
659 	// find the requirement matching the base package
660 	BPackageResolvableExpression* basePackage = NULL;
661 	int32 count = info.RequiresList().CountItems();
662 	for (int32 i = 0; i < count; i++) {
663 		BPackageResolvableExpression* requires = info.RequiresList().ItemAt(i);
664 		if (requires->Name() == info.BasePackage()) {
665 			basePackage = requires;
666 			break;
667 		}
668 	}
669 
670 	if (basePackage == NULL) {
671 		fUserInteractionHandler->Warn(B_OK, "package %s-%s doesn't have a "
672 			"matching requires for its base package \"%s\"",
673 			info.Name().String(), info.Version().ToString().String(),
674 			info.BasePackage().String());
675 		return -1;
676 	}
677 
678 	// find the first package matching the base package requires
679 	count = packages.CountItems();
680 	for (int32 i = 0; i < count; i++) {
681 		BSolverPackage* package = packages.ItemAt(i);
682 		if (package->Name() == basePackage->Name()
683 			&& package->Info().Matches(*basePackage)) {
684 			return i;
685 		}
686 	}
687 
688 	return -1;
689 }
690 
691 
692 void
693 BPackageManager::_AddInstalledRepository(InstalledRepository* repository)
694 {
695 	fInstallationInterface->InitInstalledRepository(*repository);
696 
697 	BRepositoryBuilder(*repository)
698 		.AddToSolver(fSolver, repository->Location() == fLocation);
699 	repository->SetPriority(repository->InitialPriority());
700 
701 	if (!fInstalledRepositories.AddItem(repository))
702 		throw std::bad_alloc();
703 }
704 
705 
706 void
707 BPackageManager::_AddRemoteRepository(BPackageRoster& roster, const char* name,
708 	bool refresh)
709 {
710 	BRepositoryConfig config;
711 	status_t error = roster.GetRepositoryConfig(name, &config);
712 	if (error != B_OK) {
713 		fUserInteractionHandler->Warn(error,
714 			"failed to get config for repository \"%s\". Skipping.", name);
715 		return;
716 	}
717 
718 	BRepositoryCache cache;
719 	error = _GetRepositoryCache(roster, config, refresh, cache);
720 	if (error != B_OK) {
721 		fUserInteractionHandler->Warn(error,
722 			"failed to get cache for repository \"%s\". Skipping.", name);
723 		return;
724 	}
725 
726 	RemoteRepository* repository = new RemoteRepository(config);
727 	if (!fOtherRepositories.AddItem(repository)) {
728 		delete repository;
729 		throw std::bad_alloc();
730 	}
731 
732 	BRepositoryBuilder(*repository, cache, config.Name())
733 		.AddToSolver(fSolver, false);
734 }
735 
736 
737 status_t
738 BPackageManager::_GetRepositoryCache(BPackageRoster& roster,
739 	const BRepositoryConfig& config, bool refresh, BRepositoryCache& _cache)
740 {
741 	if (!refresh && roster.GetRepositoryCache(config.Name(), &_cache) == B_OK)
742 		return B_OK;
743 
744 	status_t error = RefreshRepository(config);
745 	if (error != B_OK) {
746 		fUserInteractionHandler->Warn(error,
747 			"refreshing repository \"%s\" failed", config.Name().String());
748 	}
749 
750 	return roster.GetRepositoryCache(config.Name(), &_cache);
751 }
752 
753 
754 void
755 BPackageManager::_AddPackageSpecifiers(const char* const* searchStrings,
756 	int searchStringCount, BSolverPackageSpecifierList& specifierList)
757 {
758 	for (int i = 0; i < searchStringCount; i++) {
759 		const char* searchString = searchStrings[i];
760 		if (_IsLocalPackage(searchString)) {
761 			BSolverPackage* package = _AddLocalPackage(searchString);
762 			if (!specifierList.AppendSpecifier(package))
763 				throw std::bad_alloc();
764 		} else {
765 			if (!specifierList.AppendSpecifier(searchString))
766 				throw std::bad_alloc();
767 		}
768 	}
769 }
770 
771 
772 bool
773 BPackageManager::_IsLocalPackage(const char* fileName)
774 {
775 	// Simple heuristic: fileName contains ".hpkg" and there's actually a file
776 	// it refers to.
777 	struct stat st;
778 	return strstr(fileName, ".hpkg") != NULL && stat(fileName, &st) == 0
779 		&& S_ISREG(st.st_mode);
780 }
781 
782 
783 BSolverPackage*
784 BPackageManager::_AddLocalPackage(const char* fileName)
785 {
786 	if (fLocalRepository == NULL)
787 		throw std::bad_alloc();
788 	return fLocalRepository->AddLocalPackage(fileName);
789 }
790 
791 
792 bool
793 BPackageManager::_NextSpecificInstallationLocation()
794 {
795 	try {
796 		if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) {
797 			fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME;
798 			fSystemRepository->SetInstalled(false);
799 			_AddInstalledRepository(fHomeRepository);
800 			return true;
801 		}
802 	} catch (BFatalErrorException& e) {
803 		// No home repo. This is acceptable for example when we are in an haikuporter chroot.
804 	}
805 
806 	return false;
807 }
808 
809 
810 status_t
811 BPackageManager::DownloadPackage(const BString& fileURL,
812 	const BEntry& targetEntry, const BString& checksum)
813 {
814 	BDecisionProvider provider;
815 	BContext context(provider, *this);
816 	return DownloadFileRequest(context, fileURL, targetEntry, checksum)
817 		.Process();
818 }
819 
820 
821 status_t
822 BPackageManager::RefreshRepository(const BRepositoryConfig& repoConfig)
823 {
824 	BDecisionProvider provider;
825 	BContext context(provider, *this);
826 	return BRefreshRepositoryRequest(context, repoConfig).Process();
827 }
828 
829 
830 // #pragma mark - RemoteRepository
831 
832 
833 BPackageManager::RemoteRepository::RemoteRepository(
834 	const BRepositoryConfig& config)
835 	:
836 	BSolverRepository(),
837 	fConfig(config)
838 {
839 }
840 
841 
842 const BRepositoryConfig&
843 BPackageManager::RemoteRepository::Config() const
844 {
845 	return fConfig;
846 }
847 
848 
849 // #pragma mark - LocalRepository
850 
851 
852 BPackageManager::LocalRepository::LocalRepository()
853 	:
854 	BSolverRepository()
855 {
856 }
857 
858 
859 BPackageManager::LocalRepository::LocalRepository(const BString& name)
860 	:
861 	BSolverRepository(name)
862 {
863 }
864 
865 
866 // #pragma mark - MiscLocalRepository
867 
868 
869 BPackageManager::MiscLocalRepository::MiscLocalRepository()
870 	:
871 	LocalRepository("local"),
872 	fPackagePaths()
873 {
874 	SetPriority(-127);
875 }
876 
877 
878 BSolverPackage*
879 BPackageManager::MiscLocalRepository::AddLocalPackage(const char* fileName)
880 {
881 	BSolverPackage* package;
882 	BRepositoryBuilder(*this).AddPackage(fileName, &package);
883 
884 	fPackagePaths[package] = fileName;
885 
886 	return package;
887 }
888 
889 
890 void
891 BPackageManager::MiscLocalRepository::GetPackagePath(BSolverPackage* package,
892 	BPath& _path)
893 {
894 	PackagePathMap::const_iterator it = fPackagePaths.find(package);
895 	if (it == fPackagePaths.end()) {
896 		DIE("package %s not in local repository",
897 			package->VersionedName().String());
898 	}
899 
900 	status_t error = _path.SetTo(it->second.c_str());
901 	if (error != B_OK)
902 		DIE(error, "failed to init package path %s", it->second.c_str());
903 }
904 
905 
906 // #pragma mark - InstalledRepository
907 
908 
909 BPackageManager::InstalledRepository::InstalledRepository(const char* name,
910 	BPackageInstallationLocation location, int32 priority)
911 	:
912 	LocalRepository(),
913 	fDisabledPackages(10, true),
914 	fPackagesToActivate(),
915 	fPackagesToDeactivate(),
916 	fInitialName(name),
917 	fLocation(location),
918 	fInitialPriority(priority)
919 {
920 }
921 
922 
923 void
924 BPackageManager::InstalledRepository::GetPackagePath(BSolverPackage* package,
925 	BPath& _path)
926 {
927 	directory_which packagesWhich;
928 	switch (fLocation) {
929 		case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM:
930 			packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY;
931 			break;
932 		case B_PACKAGE_INSTALLATION_LOCATION_HOME:
933 			packagesWhich = B_USER_PACKAGES_DIRECTORY;
934 			break;
935 		default:
936 			DIE("don't know packages directory path for installation location "
937 				"\"%s\"", Name().String());
938 	}
939 
940 	BString fileName(package->Info().FileName());
941 	status_t error = find_directory(packagesWhich, &_path);
942 	if (error != B_OK || (error = _path.Append(fileName)) != B_OK) {
943 		DIE(error, "failed to get path of package file \"%s\" in installation "
944 			"location \"%s\"", fileName.String(), Name().String());
945 	}
946 }
947 
948 
949 void
950 BPackageManager::InstalledRepository::DisablePackage(BSolverPackage* package)
951 {
952 	if (fDisabledPackages.HasItem(package))
953 		DIE("package %s already disabled", package->VersionedName().String());
954 
955 	if (package->Repository() != this) {
956 		DIE("package %s not in repository %s",
957 			package->VersionedName().String(), Name().String());
958 	}
959 
960 	// move to disabled list
961 	if (!fDisabledPackages.AddItem(package))
962 		throw std::bad_alloc();
963 
964 	RemovePackage(package);
965 }
966 
967 
968 bool
969 BPackageManager::InstalledRepository::EnablePackage(BSolverPackage* package)
970 {
971 	return fDisabledPackages.RemoveItem(package);
972 }
973 
974 
975 bool
976 BPackageManager::InstalledRepository::HasChanges() const
977 {
978 	return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty();
979 }
980 
981 
982 void
983 BPackageManager::InstalledRepository::ApplyChanges()
984 {
985 	// disable packages to deactivate
986 	for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i);
987 		i++) {
988 		if (!fDisabledPackages.HasItem(package))
989 			DisablePackage(package);
990 	}
991 
992 	// add packages to activate
993 	for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i);
994 		i++) {
995 		status_t error = AddPackage(package->Info());
996 		if (error != B_OK) {
997 			DIE(error, "failed to add package %s to %s repository",
998 				package->Name().String(), Name().String());
999 		}
1000 	}
1001 }
1002 
1003 
1004 // #pragma mark - Transaction
1005 
1006 
1007 BPackageManager::Transaction::Transaction(InstalledRepository& repository)
1008 	:
1009 	fRepository(repository),
1010 	fTransaction(),
1011 	fTransactionDirectory()
1012 {
1013 }
1014 
1015 
1016 BPackageManager::Transaction::~Transaction()
1017 {
1018 }
1019 
1020 
1021 // #pragma mark - InstallationInterface
1022 
1023 
1024 BPackageManager::InstallationInterface::~InstallationInterface()
1025 {
1026 }
1027 
1028 
1029 void
1030 BPackageManager::InstallationInterface::ResultComputed(
1031 	InstalledRepository& repository)
1032 {
1033 }
1034 
1035 
1036 // #pragma mark - ClientInstallationInterface
1037 
1038 
1039 BPackageManager::ClientInstallationInterface::ClientInstallationInterface()
1040 	:
1041 	fDaemonClient()
1042 {
1043 }
1044 
1045 
1046 BPackageManager::ClientInstallationInterface::~ClientInstallationInterface()
1047 {
1048 }
1049 
1050 
1051 void
1052 BPackageManager::ClientInstallationInterface::InitInstalledRepository(
1053 	InstalledRepository& repository)
1054 {
1055 	const char* name = repository.InitialName();
1056 	BRepositoryBuilder(repository, name)
1057 		.AddPackages(repository.Location(), name);
1058 }
1059 
1060 
1061 status_t
1062 BPackageManager::ClientInstallationInterface::PrepareTransaction(
1063 	Transaction& transaction)
1064 {
1065 	return fDaemonClient.CreateTransaction(transaction.Repository().Location(),
1066 		transaction.ActivationTransaction(),
1067 		transaction.TransactionDirectory());
1068 }
1069 
1070 
1071 status_t
1072 BPackageManager::ClientInstallationInterface::CommitTransaction(
1073 	Transaction& transaction, BCommitTransactionResult& _result)
1074 {
1075 	return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(),
1076 		_result);
1077 }
1078 
1079 
1080 // #pragma mark - UserInteractionHandler
1081 
1082 
1083 BPackageManager::UserInteractionHandler::~UserInteractionHandler()
1084 {
1085 }
1086 
1087 
1088 void
1089 BPackageManager::UserInteractionHandler::HandleProblems()
1090 {
1091 	throw BAbortedByUserException();
1092 }
1093 
1094 
1095 void
1096 BPackageManager::UserInteractionHandler::ConfirmChanges(bool fromMostSpecific)
1097 {
1098 	throw BAbortedByUserException();
1099 }
1100 
1101 
1102 void
1103 BPackageManager::UserInteractionHandler::Warn(status_t error,
1104 	const char* format, ...)
1105 {
1106 }
1107 
1108 
1109 void
1110 BPackageManager::UserInteractionHandler::ProgressPackageDownloadStarted(
1111 	const char* packageName)
1112 {
1113 }
1114 
1115 
1116 void
1117 BPackageManager::UserInteractionHandler::ProgressPackageDownloadActive(
1118 	const char* packageName, float completionPercentage, off_t bytes,
1119 	off_t totalBytes)
1120 {
1121 }
1122 
1123 
1124 void
1125 BPackageManager::UserInteractionHandler::ProgressPackageDownloadComplete(
1126 	const char* packageName)
1127 {
1128 }
1129 
1130 
1131 void
1132 BPackageManager::UserInteractionHandler::ProgressPackageChecksumStarted(
1133 	const char* title)
1134 {
1135 }
1136 
1137 
1138 void
1139 BPackageManager::UserInteractionHandler::ProgressPackageChecksumComplete(
1140 	const char* title)
1141 {
1142 }
1143 
1144 
1145 void
1146 BPackageManager::UserInteractionHandler::ProgressStartApplyingChanges(
1147 	InstalledRepository& repository)
1148 {
1149 }
1150 
1151 
1152 void
1153 BPackageManager::UserInteractionHandler::ProgressTransactionCommitted(
1154 	InstalledRepository& repository, const BCommitTransactionResult& result)
1155 {
1156 }
1157 
1158 
1159 void
1160 BPackageManager::UserInteractionHandler::ProgressApplyingChangesDone(
1161 	InstalledRepository& repository)
1162 {
1163 }
1164 
1165 
1166 }	// namespace BPrivate
1167 
1168 }	// namespace BManager
1169 
1170 }	// namespace BPackageKit
1171