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 using BPackageKit::BPrivate::FetchUtils;
38 #include "PackageManagerUtils.h"
39
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "PackageManagerKit"
42
43
44 using BPackageKit::BPrivate::FetchFileJob;
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
BPackageManager(BPackageInstallationLocation location,InstallationInterface * installationInterface,UserInteractionHandler * userInteractionHandler)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
~BPackageManager()79 BPackageManager::~BPackageManager()
80 {
81 delete fSolver;
82 delete fSystemRepository;
83 delete fHomeRepository;
84 delete fLocalRepository;
85 }
86
87
88 void
Init(uint32 flags)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
SetDebugLevel(int32 level)150 BPackageManager::SetDebugLevel(int32 level)
151 {
152 fDebugLevel = level;
153
154 if (fSolver != NULL)
155 fSolver->SetDebugLevel(fDebugLevel);
156 }
157
158
159 void
Install(const char * const * packages,int packageCount)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
Install(const BSolverPackageSpecifierList & packages)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
Uninstall(const char * const * packages,int packageCount)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
Uninstall(const BSolverPackageSpecifierList & packages)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
Update(const char * const * packages,int packageCount)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
Update(const BSolverPackageSpecifierList & packages)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
FullSync()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
VerifyInstallation()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&
InstallationRepository()370 BPackageManager::InstallationRepository()
371 {
372 if (fInstalledRepositories.IsEmpty())
373 DIE("No installation repository");
374
375 return *fInstalledRepositories.LastItem();
376 }
377
378
379 void
JobStarted(BSupportKit::BJob * job)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
JobProgress(BSupportKit::BJob * job)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
JobSucceeded(BSupportKit::BJob * job)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
_HandleProblems()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
_AnalyzeResult()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
_ConfirmChanges(bool fromMostSpecific)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
_ApplyPackageChanges(bool fromMostSpecific)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
_PreparePackageChanges(InstalledRepository & installationRepository)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 reusingDownload = 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 reusingDownload = true;
597 printf("Re-using download '%s' from previous "
598 "transaction%s\n", bestFile,
599 FetchUtils::IsDownloadCompleted(
600 path.Path()) ? "" : " (partial)");
601 }
602 globfree(&globbuf);
603 }
604 }
605
606 // download the package (this will resume the download if the
607 // file already exists)
608 BString url = remoteRepository->Config().PackagesURL();
609 url << '/' << fileName;
610
611 status_t error;
612 retryDownload:
613 error = DownloadPackage(url, entry,
614 package->Info().Checksum());
615 if (error != B_OK) {
616 if (error == B_BAD_DATA || error == ERANGE) {
617 // B_BAD_DATA is returned when there is a checksum
618 // mismatch. Make sure this download is not re-used.
619 entry.Remove();
620
621 if (reusingDownload) {
622 // Maybe the download we reused had some problem.
623 // Try again, this time without reusing the download.
624 printf("\nPrevious download '%s' was invalid. Redownloading.\n",
625 path.Path());
626 reusingDownload = false;
627 goto retryDownload;
628 }
629 }
630 DIE(error, "Failed to download package %s",
631 package->Info().Name().String());
632 }
633 } else if (package->Repository() != &installationRepository) {
634 // clone the existing package
635 LocalRepository* localRepository
636 = dynamic_cast<LocalRepository*>(package->Repository());
637 if (localRepository == NULL) {
638 DIE("Internal error: repository %s is not a local repository",
639 package->Repository()->Name().String());
640 }
641 _ClonePackageFile(localRepository, package, entry);
642 }
643
644 // add package to transaction
645 if (!transaction->ActivationTransaction().AddPackageToActivate(
646 fileName)) {
647 throw std::bad_alloc();
648 }
649 }
650
651 for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
652 i++) {
653 // add package to transaction
654 if (!transaction->ActivationTransaction().AddPackageToDeactivate(
655 package->Info().FileName())) {
656 throw std::bad_alloc();
657 }
658 }
659 }
660
661
662 void
_CommitPackageChanges(Transaction & transaction)663 BPackageManager::_CommitPackageChanges(Transaction& transaction)
664 {
665 InstalledRepository& installationRepository = transaction.Repository();
666
667 fUserInteractionHandler->ProgressStartApplyingChanges(
668 installationRepository);
669
670 // commit the transaction
671 BCommitTransactionResult transactionResult;
672 status_t error = fInstallationInterface->CommitTransaction(transaction,
673 transactionResult);
674 if (error != B_OK)
675 DIE(error, "Failed to commit transaction");
676 if (transactionResult.Error() != B_TRANSACTION_OK)
677 DIE(transactionResult);
678
679 fUserInteractionHandler->ProgressTransactionCommitted(
680 installationRepository, transactionResult);
681
682 BEntry transactionDirectoryEntry;
683 if ((error = transaction.TransactionDirectory()
684 .GetEntry(&transactionDirectoryEntry)) != B_OK
685 || (error = transactionDirectoryEntry.Remove()) != B_OK) {
686 fUserInteractionHandler->Warn(error,
687 B_TRANSLATE("Failed to remove transaction directory"));
688 }
689
690 fUserInteractionHandler->ProgressApplyingChangesDone(
691 installationRepository);
692 }
693
694
695 void
_ClonePackageFile(LocalRepository * repository,BSolverPackage * package,const BEntry & entry)696 BPackageManager::_ClonePackageFile(LocalRepository* repository,
697 BSolverPackage* package, const BEntry& entry)
698 {
699 // get source and destination path
700 BPath sourcePath;
701 repository->GetPackagePath(package, sourcePath);
702
703 BPath destinationPath;
704 status_t error = entry.GetPath(&destinationPath);
705 if (error != B_OK) {
706 DIE(error, "Failed to entry path of package file to install \"%s\"",
707 package->Info().FileName().String());
708 }
709
710 // Copy the package. Ideally we would just hard-link it, but BFS doesn't
711 // support that.
712 error = BCopyEngine().CopyEntry(sourcePath.Path(), destinationPath.Path());
713 if (error != B_OK)
714 DIE(error, "Failed to copy package file \"%s\"", sourcePath.Path());
715 }
716
717
718 int32
_FindBasePackage(const PackageList & packages,const BPackageInfo & info)719 BPackageManager::_FindBasePackage(const PackageList& packages,
720 const BPackageInfo& info)
721 {
722 if (info.BasePackage().IsEmpty())
723 return -1;
724
725 // find the requirement matching the base package
726 BPackageResolvableExpression* basePackage = NULL;
727 int32 count = info.RequiresList().CountItems();
728 for (int32 i = 0; i < count; i++) {
729 BPackageResolvableExpression* require = info.RequiresList().ItemAt(i);
730 if (require->Name() == info.BasePackage()) {
731 basePackage = require;
732 break;
733 }
734 }
735
736 if (basePackage == NULL) {
737 fUserInteractionHandler->Warn(B_OK, B_TRANSLATE("Package %s-%s "
738 "doesn't have a matching requires for its base package \"%s\""),
739 info.Name().String(), info.Version().ToString().String(),
740 info.BasePackage().String());
741 return -1;
742 }
743
744 // find the first package matching the base package requires
745 count = packages.CountItems();
746 for (int32 i = 0; i < count; i++) {
747 BSolverPackage* package = packages.ItemAt(i);
748 if (package->Name() == basePackage->Name()
749 && package->Info().Matches(*basePackage)) {
750 return i;
751 }
752 }
753
754 return -1;
755 }
756
757
758 void
_AddInstalledRepository(InstalledRepository * repository)759 BPackageManager::_AddInstalledRepository(InstalledRepository* repository)
760 {
761 fInstallationInterface->InitInstalledRepository(*repository);
762
763 BRepositoryBuilder(*repository)
764 .AddToSolver(fSolver, repository->Location() == fLocation);
765 repository->SetPriority(repository->InitialPriority());
766
767 if (!fInstalledRepositories.AddItem(repository))
768 throw std::bad_alloc();
769 }
770
771
772 void
_AddRemoteRepository(BPackageRoster & roster,const char * name,bool refresh)773 BPackageManager::_AddRemoteRepository(BPackageRoster& roster, const char* name,
774 bool refresh)
775 {
776 BRepositoryConfig config;
777 status_t error = roster.GetRepositoryConfig(name, &config);
778 if (error != B_OK) {
779 fUserInteractionHandler->Warn(error, B_TRANSLATE(
780 "Failed to get config for repository \"%s\". Skipping."), name);
781 return;
782 }
783
784 BRepositoryCache cache;
785 error = _GetRepositoryCache(roster, config, refresh, cache);
786 if (error != B_OK) {
787 fUserInteractionHandler->Warn(error, B_TRANSLATE(
788 "Failed to get cache for repository \"%s\". Skipping."), name);
789 return;
790 }
791
792 RemoteRepository* repository = new RemoteRepository(config);
793 if (!fOtherRepositories.AddItem(repository)) {
794 delete repository;
795 throw std::bad_alloc();
796 }
797
798 BRepositoryBuilder(*repository, cache, config.Name())
799 .AddToSolver(fSolver, false);
800 }
801
802
803 status_t
_GetRepositoryCache(BPackageRoster & roster,const BRepositoryConfig & config,bool refresh,BRepositoryCache & _cache)804 BPackageManager::_GetRepositoryCache(BPackageRoster& roster,
805 const BRepositoryConfig& config, bool refresh, BRepositoryCache& _cache)
806 {
807 if (!refresh && roster.GetRepositoryCache(config.Name(), &_cache) == B_OK)
808 return B_OK;
809
810 status_t error = RefreshRepository(config);
811 if (error != B_OK) {
812 fUserInteractionHandler->Warn(error, B_TRANSLATE(
813 "Refreshing repository \"%s\" failed"), config.Name().String());
814 }
815
816 return roster.GetRepositoryCache(config.Name(), &_cache);
817 }
818
819
820 void
_AddPackageSpecifiers(const char * const * searchStrings,int searchStringCount,BSolverPackageSpecifierList & specifierList)821 BPackageManager::_AddPackageSpecifiers(const char* const* searchStrings,
822 int searchStringCount, BSolverPackageSpecifierList& specifierList)
823 {
824 for (int i = 0; i < searchStringCount; i++) {
825 const char* searchString = searchStrings[i];
826 if (_IsLocalPackage(searchString)) {
827 BSolverPackage* package = _AddLocalPackage(searchString);
828 if (!specifierList.AppendSpecifier(package))
829 throw std::bad_alloc();
830 } else {
831 if (!specifierList.AppendSpecifier(searchString))
832 throw std::bad_alloc();
833 }
834 }
835 }
836
837
838 bool
_IsLocalPackage(const char * fileName)839 BPackageManager::_IsLocalPackage(const char* fileName)
840 {
841 // Simple heuristic: fileName contains ".hpkg" and there's actually a file
842 // it refers to.
843 struct stat st;
844 return strstr(fileName, ".hpkg") != NULL && stat(fileName, &st) == 0
845 && S_ISREG(st.st_mode);
846 }
847
848
849 BSolverPackage*
_AddLocalPackage(const char * fileName)850 BPackageManager::_AddLocalPackage(const char* fileName)
851 {
852 if (fLocalRepository == NULL)
853 throw std::bad_alloc();
854 return fLocalRepository->AddLocalPackage(fileName);
855 }
856
857
858 bool
_NextSpecificInstallationLocation()859 BPackageManager::_NextSpecificInstallationLocation()
860 {
861 try {
862 if (fLocation == B_PACKAGE_INSTALLATION_LOCATION_SYSTEM) {
863 fLocation = B_PACKAGE_INSTALLATION_LOCATION_HOME;
864 fSystemRepository->SetInstalled(false);
865 _AddInstalledRepository(fHomeRepository);
866 return true;
867 }
868 } catch (BFatalErrorException& e) {
869 // No home repo. This is acceptable for example when we are in an haikuporter chroot.
870 }
871
872 return false;
873 }
874
875
876 status_t
DownloadPackage(const BString & fileURL,const BEntry & targetEntry,const BString & checksum)877 BPackageManager::DownloadPackage(const BString& fileURL,
878 const BEntry& targetEntry, const BString& checksum)
879 {
880 BDecisionProvider provider;
881 BContext context(provider, *this);
882 return DownloadFileRequest(context, fileURL, targetEntry, checksum)
883 .Process();
884 }
885
886
887 status_t
RefreshRepository(const BRepositoryConfig & repoConfig)888 BPackageManager::RefreshRepository(const BRepositoryConfig& repoConfig)
889 {
890 BDecisionProvider provider;
891 BContext context(provider, *this);
892 return BRefreshRepositoryRequest(context, repoConfig).Process();
893 }
894
895
896 // #pragma mark - RemoteRepository
897
898
RemoteRepository(const BRepositoryConfig & config)899 BPackageManager::RemoteRepository::RemoteRepository(
900 const BRepositoryConfig& config)
901 :
902 BSolverRepository(),
903 fConfig(config)
904 {
905 }
906
907
908 const BRepositoryConfig&
Config() const909 BPackageManager::RemoteRepository::Config() const
910 {
911 return fConfig;
912 }
913
914
915 // #pragma mark - LocalRepository
916
917
LocalRepository()918 BPackageManager::LocalRepository::LocalRepository()
919 :
920 BSolverRepository()
921 {
922 }
923
924
LocalRepository(const BString & name)925 BPackageManager::LocalRepository::LocalRepository(const BString& name)
926 :
927 BSolverRepository(name)
928 {
929 }
930
931
932 // #pragma mark - MiscLocalRepository
933
934
MiscLocalRepository()935 BPackageManager::MiscLocalRepository::MiscLocalRepository()
936 :
937 LocalRepository("local"),
938 fPackagePaths()
939 {
940 SetPriority(-127);
941 }
942
943
944 BSolverPackage*
AddLocalPackage(const char * fileName)945 BPackageManager::MiscLocalRepository::AddLocalPackage(const char* fileName)
946 {
947 BSolverPackage* package;
948 BRepositoryBuilder(*this).AddPackage(fileName, &package);
949
950 fPackagePaths[package] = fileName;
951
952 return package;
953 }
954
955
956 void
GetPackagePath(BSolverPackage * package,BPath & _path)957 BPackageManager::MiscLocalRepository::GetPackagePath(BSolverPackage* package,
958 BPath& _path)
959 {
960 PackagePathMap::const_iterator it = fPackagePaths.find(package);
961 if (it == fPackagePaths.end()) {
962 DIE("Package %s not in local repository",
963 package->VersionedName().String());
964 }
965
966 status_t error = _path.SetTo(it->second.c_str());
967 if (error != B_OK)
968 DIE(error, "Failed to init package path %s", it->second.c_str());
969 }
970
971
972 // #pragma mark - InstalledRepository
973
974
InstalledRepository(const char * name,BPackageInstallationLocation location,int32 priority)975 BPackageManager::InstalledRepository::InstalledRepository(const char* name,
976 BPackageInstallationLocation location, int32 priority)
977 :
978 LocalRepository(),
979 fDisabledPackages(10, true),
980 fPackagesToActivate(),
981 fPackagesToDeactivate(),
982 fInitialName(name),
983 fLocation(location),
984 fInitialPriority(priority)
985 {
986 }
987
988
989 void
GetPackagePath(BSolverPackage * package,BPath & _path)990 BPackageManager::InstalledRepository::GetPackagePath(BSolverPackage* package,
991 BPath& _path)
992 {
993 directory_which packagesWhich;
994 switch (fLocation) {
995 case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM:
996 packagesWhich = B_SYSTEM_PACKAGES_DIRECTORY;
997 break;
998 case B_PACKAGE_INSTALLATION_LOCATION_HOME:
999 packagesWhich = B_USER_PACKAGES_DIRECTORY;
1000 break;
1001 default:
1002 DIE("Don't know packages directory path for installation location "
1003 "\"%s\"", Name().String());
1004 }
1005
1006 BString fileName(package->Info().FileName());
1007 status_t error = find_directory(packagesWhich, &_path);
1008 if (error != B_OK || (error = _path.Append(fileName)) != B_OK) {
1009 DIE(error, "Failed to get path of package file \"%s\" in installation "
1010 "location \"%s\"", fileName.String(), Name().String());
1011 }
1012 }
1013
1014
1015 void
DisablePackage(BSolverPackage * package)1016 BPackageManager::InstalledRepository::DisablePackage(BSolverPackage* package)
1017 {
1018 if (fDisabledPackages.HasItem(package))
1019 DIE("Package %s already disabled", package->VersionedName().String());
1020
1021 if (package->Repository() != this) {
1022 DIE("Package %s not in repository %s",
1023 package->VersionedName().String(), Name().String());
1024 }
1025
1026 // move to disabled list
1027 if (!fDisabledPackages.AddItem(package))
1028 throw std::bad_alloc();
1029
1030 RemovePackage(package);
1031 }
1032
1033
1034 bool
EnablePackage(BSolverPackage * package)1035 BPackageManager::InstalledRepository::EnablePackage(BSolverPackage* package)
1036 {
1037 return fDisabledPackages.RemoveItem(package);
1038 }
1039
1040
1041 bool
HasChanges() const1042 BPackageManager::InstalledRepository::HasChanges() const
1043 {
1044 return !fPackagesToActivate.IsEmpty() || !fPackagesToDeactivate.IsEmpty();
1045 }
1046
1047
1048 void
ApplyChanges()1049 BPackageManager::InstalledRepository::ApplyChanges()
1050 {
1051 // disable packages to deactivate
1052 for (int32 i = 0; BSolverPackage* package = fPackagesToDeactivate.ItemAt(i);
1053 i++) {
1054 if (!fDisabledPackages.HasItem(package))
1055 DisablePackage(package);
1056 }
1057
1058 // add packages to activate
1059 for (int32 i = 0; BSolverPackage* package = fPackagesToActivate.ItemAt(i);
1060 i++) {
1061 status_t error = AddPackage(package->Info());
1062 if (error != B_OK) {
1063 DIE(error, "Failed to add package %s to %s repository",
1064 package->Name().String(), Name().String());
1065 }
1066 }
1067 }
1068
1069
1070 // #pragma mark - Transaction
1071
1072
Transaction(InstalledRepository & repository)1073 BPackageManager::Transaction::Transaction(InstalledRepository& repository)
1074 :
1075 fRepository(repository),
1076 fTransaction(),
1077 fTransactionDirectory()
1078 {
1079 }
1080
1081
~Transaction()1082 BPackageManager::Transaction::~Transaction()
1083 {
1084 }
1085
1086
1087 // #pragma mark - InstallationInterface
1088
1089
~InstallationInterface()1090 BPackageManager::InstallationInterface::~InstallationInterface()
1091 {
1092 }
1093
1094
1095 void
ResultComputed(InstalledRepository & repository)1096 BPackageManager::InstallationInterface::ResultComputed(
1097 InstalledRepository& repository)
1098 {
1099 }
1100
1101
1102 // #pragma mark - ClientInstallationInterface
1103
1104
ClientInstallationInterface()1105 BPackageManager::ClientInstallationInterface::ClientInstallationInterface()
1106 :
1107 fDaemonClient()
1108 {
1109 }
1110
1111
~ClientInstallationInterface()1112 BPackageManager::ClientInstallationInterface::~ClientInstallationInterface()
1113 {
1114 }
1115
1116
1117 void
InitInstalledRepository(InstalledRepository & repository)1118 BPackageManager::ClientInstallationInterface::InitInstalledRepository(
1119 InstalledRepository& repository)
1120 {
1121 const char* name = repository.InitialName();
1122 BRepositoryBuilder(repository, name)
1123 .AddPackages(repository.Location(), name);
1124 }
1125
1126
1127 status_t
PrepareTransaction(Transaction & transaction)1128 BPackageManager::ClientInstallationInterface::PrepareTransaction(
1129 Transaction& transaction)
1130 {
1131 return fDaemonClient.CreateTransaction(transaction.Repository().Location(),
1132 transaction.ActivationTransaction(),
1133 transaction.TransactionDirectory());
1134 }
1135
1136
1137 status_t
CommitTransaction(Transaction & transaction,BCommitTransactionResult & _result)1138 BPackageManager::ClientInstallationInterface::CommitTransaction(
1139 Transaction& transaction, BCommitTransactionResult& _result)
1140 {
1141 return fDaemonClient.CommitTransaction(transaction.ActivationTransaction(),
1142 _result);
1143 }
1144
1145
1146 // #pragma mark - UserInteractionHandler
1147
1148
~UserInteractionHandler()1149 BPackageManager::UserInteractionHandler::~UserInteractionHandler()
1150 {
1151 }
1152
1153
1154 void
HandleProblems()1155 BPackageManager::UserInteractionHandler::HandleProblems()
1156 {
1157 throw BAbortedByUserException();
1158 }
1159
1160
1161 void
ConfirmChanges(bool fromMostSpecific)1162 BPackageManager::UserInteractionHandler::ConfirmChanges(bool fromMostSpecific)
1163 {
1164 throw BAbortedByUserException();
1165 }
1166
1167
1168 void
Warn(status_t error,const char * format,...)1169 BPackageManager::UserInteractionHandler::Warn(status_t error,
1170 const char* format, ...)
1171 {
1172 }
1173
1174
1175 void
ProgressPackageDownloadStarted(const char * packageName)1176 BPackageManager::UserInteractionHandler::ProgressPackageDownloadStarted(
1177 const char* packageName)
1178 {
1179 }
1180
1181
1182 void
ProgressPackageDownloadActive(const char * packageName,float completionPercentage,off_t bytes,off_t totalBytes)1183 BPackageManager::UserInteractionHandler::ProgressPackageDownloadActive(
1184 const char* packageName, float completionPercentage, off_t bytes,
1185 off_t totalBytes)
1186 {
1187 }
1188
1189
1190 void
ProgressPackageDownloadComplete(const char * packageName)1191 BPackageManager::UserInteractionHandler::ProgressPackageDownloadComplete(
1192 const char* packageName)
1193 {
1194 }
1195
1196
1197 void
ProgressPackageChecksumStarted(const char * title)1198 BPackageManager::UserInteractionHandler::ProgressPackageChecksumStarted(
1199 const char* title)
1200 {
1201 }
1202
1203
1204 void
ProgressPackageChecksumComplete(const char * title)1205 BPackageManager::UserInteractionHandler::ProgressPackageChecksumComplete(
1206 const char* title)
1207 {
1208 }
1209
1210
1211 void
ProgressStartApplyingChanges(InstalledRepository & repository)1212 BPackageManager::UserInteractionHandler::ProgressStartApplyingChanges(
1213 InstalledRepository& repository)
1214 {
1215 }
1216
1217
1218 void
ProgressTransactionCommitted(InstalledRepository & repository,const BCommitTransactionResult & result)1219 BPackageManager::UserInteractionHandler::ProgressTransactionCommitted(
1220 InstalledRepository& repository, const BCommitTransactionResult& result)
1221 {
1222 }
1223
1224
1225 void
ProgressApplyingChangesDone(InstalledRepository & repository)1226 BPackageManager::UserInteractionHandler::ProgressApplyingChangesDone(
1227 InstalledRepository& repository)
1228 {
1229 }
1230
1231
1232 } // namespace BPrivate
1233
1234 } // namespace BManager
1235
1236 } // namespace BPackageKit
1237