2 * Copyright 2013-2017, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 * Stephan Aßmus <superstippi@gmx.de>
8 * Rene Gollent <rene@gollent.com>
9 * Julian Harnath <julian.harnath@rwth-aachen.de>
13 #include "PackageManager.h"
20 #include <FindDirectory.h>
24 #include <package/DownloadFileRequest.h>
25 #include <package/manager/Exceptions.h>
26 #include <package/RefreshRepositoryRequest.h>
27 #include <package/hpkg/NoErrorOutput.h>
28 #include <package/hpkg/PackageContentHandler.h>
29 #include <package/hpkg/PackageEntry.h>
30 #include <package/hpkg/PackageEntryAttribute.h>
31 #include <package/hpkg/PackageInfoAttributeValue.h>
32 #include <package/hpkg/PackageReader.h>
33 #include <package/solver/SolverPackage.h>
34 #include <package/solver/SolverProblem.h>
35 #include <package/solver/SolverProblemSolution.h>
37 #include "AutoDeleter.h"
38 #include "AutoLocker.h"
40 #include "PackageInfo.h"
41 #include "ProblemWindow.h"
42 #include "ResultWindow.h"
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "PackageManager"
49 using namespace BPackageKit
;
50 using namespace BPackageKit::BPrivate
;
51 using namespace BPackageKit::BManager::BPrivate
;
53 using BPackageKit::BRefreshRepositoryRequest
;
54 using BPackageKit::DownloadFileRequest
;
55 using BPackageKit::BSolver
;
56 using BPackageKit::BSolverPackage
;
57 using BPackageKit::BSolverRepository
;
58 using BPackageKit::BHPKG::BNoErrorOutput
;
59 using BPackageKit::BHPKG::BPackageContentHandler
;
60 using BPackageKit::BHPKG::BPackageEntry
;
61 using BPackageKit::BHPKG::BPackageEntryAttribute
;
62 using BPackageKit::BHPKG::BPackageInfoAttributeValue
;
63 using BPackageKit::BHPKG::BPackageReader
;
66 typedef std::set
<PackageInfoRef
> PackageInfoSet
;
69 // #pragma mark - PackageProgressListener
72 PackageProgressListener::~PackageProgressListener()
78 PackageProgressListener::DownloadProgressChanged(const char* packageName
,
85 PackageProgressListener::DownloadProgressComplete(const char* packageName
)
91 PackageProgressListener::ConfirmedChanges(
92 BPackageManager::InstalledRepository
& repository
)
98 PackageProgressListener::StartApplyingChanges(
99 BPackageManager::InstalledRepository
& repository
)
105 PackageProgressListener::ApplyingChangesDone(
106 BPackageManager::InstalledRepository
& repository
)
112 // #pragma mark - InstallPackageAction
115 class InstallPackageAction
: public PackageAction
,
116 private PackageProgressListener
{
118 InstallPackageAction(PackageInfoRef package
, Model
* model
)
120 PackageAction(PACKAGE_ACTION_INSTALL
, package
, model
),
121 fLastDownloadUpdate(0)
125 virtual const char* Label() const
127 return B_TRANSLATE("Install");
130 virtual status_t
Perform()
132 fPackageManager
->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES
133 | BPackageManager::B_ADD_REMOTE_REPOSITORIES
134 | BPackageManager::B_REFRESH_REPOSITORIES
);
135 PackageInfoRef
ref(Package());
136 PackageState state
= ref
->State();
137 ref
->SetState(PENDING
);
139 fPackageManager
->SetCurrentActionPackage(ref
, true);
140 fPackageManager
->AddProgressListener(this);
143 if (ref
->IsLocalFile())
144 packageName
= ref
->LocalFilePath();
146 packageName
= ref
->Name();
148 const char* packageNameString
= packageName
.String();
150 fPackageManager
->Install(&packageNameString
, 1);
151 } catch (BFatalErrorException ex
) {
153 errorString
.SetToFormat(
154 "Fatal error occurred while installing package %s: "
155 "%s (%s)\n", packageNameString
, ex
.Message().String(),
156 ex
.Details().String());
157 BAlert
* alert(new(std::nothrow
) BAlert(B_TRANSLATE("Fatal error"),
158 errorString
, B_TRANSLATE("Close"), NULL
, NULL
,
159 B_WIDTH_AS_USUAL
, B_STOP_ALERT
));
162 _SetDownloadedPackagesState(NONE
);
163 ref
->SetState(state
);
165 } catch (BAbortedByUserException ex
) {
166 fprintf(stderr
, "Installation of package "
167 "%s aborted by user: %s\n", packageNameString
,
168 ex
.Message().String());
169 _SetDownloadedPackagesState(NONE
);
170 ref
->SetState(state
);
172 } catch (BNothingToDoException ex
) {
173 fprintf(stderr
, "Nothing to do while installing package "
174 "%s: %s\n", packageNameString
, ex
.Message().String());
176 } catch (BException ex
) {
177 fprintf(stderr
, "Exception occurred while installing package "
178 "%s: %s\n", packageNameString
, ex
.Message().String());
179 _SetDownloadedPackagesState(NONE
);
180 ref
->SetState(state
);
184 fPackageManager
->RemoveProgressListener(this);
186 _SetDownloadedPackagesState(ACTIVATED
);
191 // DownloadProgressListener
192 virtual void DownloadProgressChanged(const char* packageName
,
195 bigtime_t now
= system_time();
196 if (now
- fLastDownloadUpdate
> 250000 || progress
== 1.0) {
197 BString
tempName(packageName
);
198 tempName
.Truncate(tempName
.FindFirst('-'));
199 // strip version suffix off package filename
200 PackageInfoRef
ref(FindPackageByName(tempName
));
201 if (ref
.Get() != NULL
) {
202 ref
->SetDownloadProgress(progress
);
203 fLastDownloadUpdate
= now
;
208 virtual void DownloadProgressComplete(const char* packageName
)
210 BString
tempName(packageName
);
211 tempName
.Truncate(tempName
.FindFirst('-'));
212 // strip version suffix off package filename
213 PackageInfoRef
ref(FindPackageByName(tempName
));
214 if (ref
.Get() != NULL
) {
215 ref
->SetDownloadProgress(1.0);
216 fDownloadedPackages
.insert(ref
);
220 virtual void ConfirmedChanges(BPackageManager::InstalledRepository
&
223 BPackageManager::InstalledRepository::PackageList
& activationList
=
224 repository
.PackagesToActivate();
226 BSolverPackage
* package
= NULL
;
227 for (int32 i
= 0; (package
= activationList
.ItemAt(i
)); i
++) {
228 PackageInfoRef
ref(FindPackageByName(package
->Info().Name()));
229 if (ref
.Get() != NULL
)
230 ref
->SetState(PENDING
);
235 void _SetDownloadedPackagesState(PackageState state
)
237 for (PackageInfoSet::iterator it
= fDownloadedPackages
.begin();
238 it
!= fDownloadedPackages
.end(); ++it
) {
239 (*it
)->SetState(state
);
244 bigtime_t fLastDownloadUpdate
;
245 PackageInfoSet fDownloadedPackages
;
249 // #pragma mark - UninstallPackageAction
252 class UninstallPackageAction
: public PackageAction
,
253 private PackageProgressListener
{
255 UninstallPackageAction(PackageInfoRef package
, Model
* model
)
257 PackageAction(PACKAGE_ACTION_UNINSTALL
, package
, model
)
261 virtual const char* Label() const
263 return B_TRANSLATE("Uninstall");
266 virtual status_t
Perform()
268 fPackageManager
->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES
);
269 PackageInfoRef
ref(Package());
270 PackageState state
= ref
->State();
271 fPackageManager
->SetCurrentActionPackage(ref
, false);
272 fPackageManager
->AddProgressListener(this);
273 const char* packageName
= ref
->Name().String();
275 fPackageManager
->Uninstall(&packageName
, 1);
276 } catch (BFatalErrorException ex
) {
278 errorString
.SetToFormat(
279 "Fatal error occurred while uninstalling package %s: "
280 "%s (%s)\n", packageName
, ex
.Message().String(),
281 ex
.Details().String());
282 BAlert
* alert(new(std::nothrow
) BAlert(B_TRANSLATE("Fatal error"),
283 errorString
, B_TRANSLATE("Close"), NULL
, NULL
,
284 B_WIDTH_AS_USUAL
, B_STOP_ALERT
));
287 ref
->SetState(state
);
289 } catch (BAbortedByUserException ex
) {
291 } catch (BNothingToDoException ex
) {
293 } catch (BException ex
) {
294 fprintf(stderr
, "Exception occurred while uninstalling package "
295 "%s: %s\n", packageName
, ex
.Message().String());
296 ref
->SetState(state
);
300 fPackageManager
->RemoveProgressListener(this);
307 void StartApplyingChanges(
308 BPackageManager::InstalledRepository
& repository
)
310 BPackageManager::InstalledRepository::PackageList
& packages
311 = repository
.PackagesToDeactivate();
312 for (int32 i
= 0; i
< packages
.CountItems(); i
++) {
313 PackageInfoRef
ref(FindPackageByName(packages
.ItemAt(i
)
315 if (ref
.Get() != NULL
)
316 fRemovedPackages
.Add(ref
);
320 void ApplyingChangesDone(
321 BPackageManager::InstalledRepository
& repository
)
323 for (int32 i
= 0; i
< fRemovedPackages
.CountItems(); i
++)
324 fRemovedPackages
.ItemAt(i
)->SetState(NONE
);
328 PackageList fRemovedPackages
;
332 // #pragma mark - OpenPackageAction
340 DeskbarLink(const BString
& path
, const BString
& link
)
347 DeskbarLink(const DeskbarLink
& other
)
354 DeskbarLink
& operator=(const DeskbarLink
& other
)
363 bool operator==(const DeskbarLink
& other
)
365 return path
== other
.path
&& link
== other
.link
;
368 bool operator!=(const DeskbarLink
& other
)
370 return !(*this == other
);
378 typedef List
<DeskbarLink
, false> DeskbarLinkList
;
381 class DeskbarLinkFinder
: public BPackageContentHandler
{
383 DeskbarLinkFinder(DeskbarLinkList
& foundLinks
)
385 fDeskbarLinks(foundLinks
)
389 virtual status_t
HandleEntry(BPackageEntry
* entry
)
391 BString path
= MakePath(entry
);
392 if (path
.FindFirst("data/deskbar/menu") == 0
393 && entry
->SymlinkPath() != NULL
) {
394 printf("found deskbar entry: %s -> %s\n", path
.String(),
395 entry
->SymlinkPath());
396 fDeskbarLinks
.Add(DeskbarLink(path
, entry
->SymlinkPath()));
401 virtual status_t
HandleEntryAttribute(BPackageEntry
* entry
,
402 BPackageEntryAttribute
* attribute
)
407 virtual status_t
HandleEntryDone(BPackageEntry
* entry
)
412 virtual status_t
HandlePackageAttribute(
413 const BPackageInfoAttributeValue
& value
)
418 virtual void HandleErrorOccurred()
422 BString
MakePath(const BPackageEntry
* entry
)
425 while (entry
!= NULL
) {
427 path
.Prepend('/', 1);
428 path
.Prepend(entry
->Name());
429 entry
= entry
->Parent();
435 DeskbarLinkList
& fDeskbarLinks
;
439 class OpenPackageAction
: public PackageAction
{
441 OpenPackageAction(PackageInfoRef package
, Model
* model
,
442 const DeskbarLink
& link
)
444 PackageAction(PACKAGE_ACTION_OPEN
, package
, model
),
446 fLabel(B_TRANSLATE("Open %DeskbarLink%"))
448 BString target
= fDeskbarLink
.link
;
449 int32 lastPathSeparator
= target
.FindLast('/');
450 if (lastPathSeparator
> 0 && lastPathSeparator
+ 1 < target
.Length())
451 target
.Remove(0, lastPathSeparator
+ 1);
453 fLabel
.ReplaceAll("%DeskbarLink%", target
);
456 virtual const char* Label() const
461 virtual status_t
Perform()
465 if (fDeskbarLink
.link
.FindFirst('/') == 0) {
466 status
= path
.SetTo(fDeskbarLink
.link
);
467 printf("trying to launch (absolute link): %s\n", path
.Path());
469 int32 location
= InstallLocation();
470 if (location
== B_PACKAGE_INSTALLATION_LOCATION_SYSTEM
) {
471 status
= find_directory(B_SYSTEM_DIRECTORY
, &path
);
474 } else if (location
== B_PACKAGE_INSTALLATION_LOCATION_HOME
) {
475 status
= find_directory(B_USER_DIRECTORY
, &path
);
482 status
= path
.Append(fDeskbarLink
.path
);
484 status
= path
.GetParent(&path
);
485 if (status
== B_OK
) {
486 status
= path
.Append(fDeskbarLink
.link
, true);
487 printf("trying to launch: %s\n", path
.Path());
493 status
= get_ref_for_path(path
.Path(), &ref
);
496 status
= be_roster
->Launch(&ref
);
501 static bool FindAppToLaunch(const PackageInfoRef
& package
,
502 DeskbarLinkList
& foundLinks
)
504 if (package
.Get() == NULL
)
507 int32 installLocation
= InstallLocation(package
);
510 if (installLocation
== B_PACKAGE_INSTALLATION_LOCATION_SYSTEM
) {
511 if (find_directory(B_SYSTEM_PACKAGES_DIRECTORY
, &packagePath
)
515 } else if (installLocation
== B_PACKAGE_INSTALLATION_LOCATION_HOME
) {
516 if (find_directory(B_USER_PACKAGES_DIRECTORY
, &packagePath
)
521 printf("OpenPackageAction::FindAppToLaunch(): "
522 "unknown install location");
526 packagePath
.Append(package
->FileName());
528 BNoErrorOutput errorOutput
;
529 BPackageReader
reader(&errorOutput
);
531 status_t status
= reader
.Init(packagePath
.Path());
532 if (status
!= B_OK
) {
533 printf("OpenPackageAction::FindAppToLaunch(): "
534 "failed to init BPackageReader(%s): %s\n",
535 packagePath
.Path(), strerror(status
));
539 // Scan package contents for Deskbar links
540 DeskbarLinkFinder
contentHandler(foundLinks
);
541 status
= reader
.ParseContent(&contentHandler
);
542 if (status
!= B_OK
) {
543 printf("OpenPackageAction::FindAppToLaunch(): "
544 "failed parse package contents (%s): %s\n",
545 packagePath
.Path(), strerror(status
));
549 return foundLinks
.CountItems() > 0;
553 DeskbarLink fDeskbarLink
;
558 // #pragma mark - PackageManager
561 PackageManager::PackageManager(BPackageInstallationLocation location
)
563 BPackageManager(location
, &fClientInstallationInterface
, this),
564 BPackageManager::UserInteractionHandler(),
566 fClientInstallationInterface(),
567 fProblemWindow(NULL
),
568 fCurrentInstallPackage(NULL
),
569 fCurrentUninstallPackage(NULL
)
574 PackageManager::~PackageManager()
576 if (fProblemWindow
!= NULL
)
577 fProblemWindow
->PostMessage(B_QUIT_REQUESTED
);
582 PackageManager::GetPackageState(const PackageInfo
& package
)
584 // TODO: Fetch information from the PackageKit
590 PackageManager::GetPackageActions(PackageInfoRef package
, Model
* model
)
592 PackageActionList actionList
;
593 if (package
->IsSystemPackage() || package
->IsSystemDependency())
596 int32 state
= package
->State();
597 if (state
== ACTIVATED
|| state
== INSTALLED
) {
598 actionList
.Add(PackageActionRef(new UninstallPackageAction(
599 package
, model
), true));
601 // Add OpenPackageActions for each deskbar link found in the
603 DeskbarLinkList foundLinks
;
604 if (OpenPackageAction::FindAppToLaunch(package
, foundLinks
)
605 && foundLinks
.CountItems() < 4) {
606 for (int32 i
= 0; i
< foundLinks
.CountItems(); i
++) {
607 const DeskbarLink
& link
= foundLinks
.ItemAtFast(i
);
608 actionList
.Add(PackageActionRef(new OpenPackageAction(
609 package
, model
, link
), true));
612 } else if (state
== NONE
|| state
== UNINSTALLED
) {
613 actionList
.Add(PackageActionRef(new InstallPackageAction(package
,
616 // TODO: activation status
623 PackageManager::SetCurrentActionPackage(PackageInfoRef package
, bool install
)
625 BSolverPackage
* solverPackage
= _GetSolverPackage(package
);
626 fCurrentInstallPackage
= install
? solverPackage
: NULL
;
627 fCurrentUninstallPackage
= install
? NULL
: solverPackage
;
632 PackageManager::RefreshRepository(const BRepositoryConfig
& repoConfig
)
636 result
= BPackageManager::RefreshRepository(repoConfig
);
637 } catch (BFatalErrorException ex
) {
638 fprintf(stderr
, "Fatal error occurred while refreshing repository: "
639 "%s (%s)\n", ex
.Message().String(), ex
.Details().String());
641 } catch (BException ex
) {
642 fprintf(stderr
, "Exception occurred while refreshing "
643 "repository: %s\n", ex
.Message().String());
652 PackageManager::DownloadPackage(const BString
& fileURL
,
653 const BEntry
& targetEntry
, const BString
& checksum
)
657 result
= BPackageManager::DownloadPackage(fileURL
, targetEntry
,
659 } catch (BFatalErrorException ex
) {
660 fprintf(stderr
, "Fatal error occurred while downloading package: "
661 "%s: %s (%s)\n", fileURL
.String(), ex
.Message().String(),
662 ex
.Details().String());
664 } catch (BException ex
) {
665 fprintf(stderr
, "Exception occurred while downloading package "
666 "%s: %s\n", fileURL
.String(), ex
.Message().String());
675 PackageManager::AddProgressListener(PackageProgressListener
* listener
)
677 fPackageProgressListeners
.AddItem(listener
);
682 PackageManager::RemoveProgressListener(PackageProgressListener
* listener
)
684 fPackageProgressListeners
.RemoveItem(listener
);
689 PackageManager::HandleProblems()
691 if (fProblemWindow
== NULL
)
692 fProblemWindow
= new ProblemWindow
;
694 ProblemWindow::SolverPackageSet installPackages
;
695 ProblemWindow::SolverPackageSet uninstallPackages
;
696 if (fCurrentInstallPackage
!= NULL
)
697 installPackages
.insert(fCurrentInstallPackage
);
699 if (fCurrentUninstallPackage
!= NULL
)
700 uninstallPackages
.insert(fCurrentUninstallPackage
);
702 if (!fProblemWindow
->Go(fSolver
,installPackages
, uninstallPackages
))
703 throw BAbortedByUserException();
708 PackageManager::ConfirmChanges(bool fromMostSpecific
)
710 ResultWindow
* window
= new ResultWindow
;
711 ObjectDeleter
<ResultWindow
> windowDeleter(window
);
713 bool hasOtherChanges
= false;
714 int32 count
= fInstalledRepositories
.CountItems();
716 if (fromMostSpecific
) {
717 for (int32 i
= count
- 1; i
>= 0; i
--)
719 |= _AddResults(*fInstalledRepositories
.ItemAt(i
), window
);
721 for (int32 i
= 0; i
< count
; i
++)
723 |= _AddResults(*fInstalledRepositories
.ItemAt(i
), window
);
726 if (!hasOtherChanges
) {
727 _NotifyChangesConfirmed();
732 if (windowDeleter
.Detach()->Go() == 0)
733 throw BAbortedByUserException();
735 _NotifyChangesConfirmed();
740 PackageManager::Warn(status_t error
, const char* format
, ...)
742 // TODO: Show alert to user
745 va_start(args
, format
);
746 vfprintf(stderr
, format
, args
);
752 printf(": %s\n", strerror(error
));
757 PackageManager::ProgressPackageDownloadStarted(const char* packageName
)
759 ProgressPackageDownloadActive(packageName
, 0.0f
, 0, 0);
764 PackageManager::ProgressPackageDownloadActive(const char* packageName
,
765 float completionPercentage
, off_t bytes
, off_t totalBytes
)
767 for (int32 i
= 0; i
< fPackageProgressListeners
.CountItems(); i
++) {
768 fPackageProgressListeners
.ItemAt(i
)->DownloadProgressChanged(
769 packageName
, completionPercentage
);
775 PackageManager::ProgressPackageDownloadComplete(const char* packageName
)
777 for (int32 i
= 0; i
< fPackageProgressListeners
.CountItems(); i
++) {
778 fPackageProgressListeners
.ItemAt(i
)->DownloadProgressComplete(
785 PackageManager::ProgressPackageChecksumStarted(const char* title
)
792 PackageManager::ProgressPackageChecksumComplete(const char* title
)
799 PackageManager::ProgressStartApplyingChanges(InstalledRepository
& repository
)
801 for (int32 i
= 0; i
< fPackageProgressListeners
.CountItems(); i
++)
802 fPackageProgressListeners
.ItemAt(i
)->StartApplyingChanges(repository
);
807 PackageManager::ProgressTransactionCommitted(InstalledRepository
& repository
,
808 const BCommitTransactionResult
& result
)
815 PackageManager::ProgressApplyingChangesDone(InstalledRepository
& repository
)
817 for (int32 i
= 0; i
< fPackageProgressListeners
.CountItems(); i
++)
818 fPackageProgressListeners
.ItemAt(i
)->ApplyingChangesDone(repository
);
823 PackageManager::_AddResults(InstalledRepository
& repository
,
824 ResultWindow
* window
)
826 if (!repository
.HasChanges())
829 ProblemWindow::SolverPackageSet installPackages
;
830 ProblemWindow::SolverPackageSet uninstallPackages
;
831 if (fCurrentInstallPackage
!= NULL
)
832 installPackages
.insert(fCurrentInstallPackage
);
834 if (fCurrentUninstallPackage
!= NULL
)
835 uninstallPackages
.insert(fCurrentUninstallPackage
);
837 return window
->AddLocationChanges(repository
.Name(),
838 repository
.PackagesToActivate(), installPackages
,
839 repository
.PackagesToDeactivate(), uninstallPackages
);
844 PackageManager::_NotifyChangesConfirmed()
846 int32 count
= fInstalledRepositories
.CountItems();
847 for (int32 i
= 0; i
< count
; i
++) {
848 for (int32 j
= 0; j
< fPackageProgressListeners
.CountItems(); j
++) {
849 fPackageProgressListeners
.ItemAt(j
)->ConfirmedChanges(
850 *fInstalledRepositories
.ItemAt(i
));
857 PackageManager::_GetSolverPackage(PackageInfoRef package
)
859 int32 flags
= BSolver::B_FIND_IN_NAME
;
860 if (package
->State() == ACTIVATED
|| package
->State() == INSTALLED
)
861 flags
|= BSolver::B_FIND_INSTALLED_ONLY
;
863 BObjectList
<BSolverPackage
> packages
;
864 status_t result
= Solver()->FindPackages(package
->Name(), flags
, packages
);
865 if (result
== B_OK
) {
866 for (int32 i
= 0; i
< packages
.CountItems(); i
++) {
867 BSolverPackage
* solverPackage
= packages
.ItemAt(i
);
868 if (solverPackage
->Name() != package
->Name())
870 else if (package
->State() == NONE
871 && dynamic_cast<BPackageManager::RemoteRepository
*>(
872 solverPackage
->Repository()) == NULL
) {
875 return solverPackage
;