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