2 * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
7 #include "PackageManager.h"
10 #include <Notification.h>
11 #include <package/DownloadFileRequest.h>
12 #include <package/RefreshRepositoryRequest.h>
13 #include <package/solver/SolverPackage.h>
14 #include <package/solver/SolverPackageSpecifierList.h>
15 #include <package/solver/SolverProblem.h>
16 #include <package/solver/SolverProblemSolution.h>
18 #include <AutoDeleter.h>
19 #include <package/manager/Exceptions.h>
20 #include <package/manager/RepositoryBuilder.h>
23 #include "ProblemWindow.h"
24 #include "ResultWindow.h"
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "PackageManager"
31 using BPackageKit::BManager::BPrivate::BAbortedByUserException
;
32 using BPackageKit::BManager::BPrivate::BFatalErrorException
;
33 using BPackageKit::BManager::BPrivate::BRepositoryBuilder
;
36 PackageManager::PackageManager(Root
* root
, Volume
* volume
)
38 BPackageManager(volume
->Location(), this, this),
39 BPackageManager::UserInteractionHandler(),
43 fPackagesAddedByUser(),
44 fPackagesRemovedByUser(),
50 PackageManager::~PackageManager()
52 if (fProblemWindow
!= NULL
)
53 fProblemWindow
->PostMessage(B_QUIT_REQUESTED
);
58 PackageManager::HandleUserChanges()
60 const PackageSet
& packagesToActivate
= fVolume
->PackagesToBeActivated();
61 const PackageSet
& packagesToDeactivate
= fVolume
->PackagesToBeDeactivated();
63 if (packagesToActivate
.empty() && packagesToDeactivate
.empty())
66 if (packagesToActivate
.empty()) {
67 // only packages removed -- use uninstall mode
68 Init(B_ADD_INSTALLED_REPOSITORIES
);
70 BSolverPackageSpecifierList packagesToUninstall
;
71 for (PackageSet::const_iterator it
= packagesToDeactivate
.begin();
72 it
!= packagesToDeactivate
.end(); ++it
) {
73 BSolverPackage
* solverPackage
= _SolverPackageFor(*it
);
74 if (solverPackage
== NULL
)
77 if (!packagesToUninstall
.AppendSpecifier(solverPackage
))
78 throw std::bad_alloc();
79 fPackagesRemovedByUser
.insert(solverPackage
);
82 if (fPackagesRemovedByUser
.empty())
85 Uninstall(packagesToUninstall
);
87 // packages added and (possibly) remove -- manually add/remove those
88 // from the repository and use verify mode
89 Init(B_ADD_INSTALLED_REPOSITORIES
| B_ADD_REMOTE_REPOSITORIES
90 | B_REFRESH_REPOSITORIES
);
92 // disable and remove uninstalled packages
93 InstalledRepository
& repository
= InstallationRepository();
94 for (PackageSet::const_iterator it
= packagesToDeactivate
.begin();
95 it
!= packagesToDeactivate
.end(); ++it
) {
96 BSolverPackage
* solverPackage
= _SolverPackageFor(*it
);
97 if (solverPackage
== NULL
)
100 repository
.DisablePackage(solverPackage
);
101 if (!repository
.PackagesToDeactivate().AddItem(solverPackage
))
102 throw std::bad_alloc();
103 fPackagesRemovedByUser
.insert(solverPackage
);
107 BRepositoryBuilder
repositoryBuilder(repository
);
108 for (PackageSet::const_iterator it
= packagesToActivate
.begin();
109 it
!= packagesToActivate
.end(); ++it
) {
110 Package
* package
= *it
;
111 BSolverPackage
* solverPackage
;
112 repositoryBuilder
.AddPackage(package
->Info(), NULL
, &solverPackage
);
113 fSolverPackages
[package
] = solverPackage
;
114 if (!repository
.PackagesToActivate().AddItem(solverPackage
))
115 throw std::bad_alloc();
116 fPackagesAddedByUser
.insert(solverPackage
);
119 if (fPackagesRemovedByUser
.empty() && fPackagesAddedByUser
.empty())
122 // TODO: When packages are moved out of the packages directory, we can't create
123 // a complete "old-state" directory.
125 VerifyInstallation();
131 PackageManager::InitInstalledRepository(InstalledRepository
& repository
)
133 const char* name
= repository
.InitialName();
134 BRepositoryBuilder
repositoryBuilder(repository
, name
);
136 if (Volume
* volume
= fRoot
->GetVolume(repository
.Location())) {
137 for (PackageFileNameHashTable::Iterator it
138 = volume
->PackagesByFileNameIterator(); it
.HasNext();) {
139 Package
* package
= it
.Next();
140 if (package
->IsActive()) {
141 BSolverPackage
* solverPackage
;
142 repositoryBuilder
.AddPackage(package
->Info(), NULL
,
144 fSolverPackages
[package
] = solverPackage
;
152 PackageManager::ResultComputed(InstalledRepository
& repository
)
154 // Normalize the result, i.e. remove the packages added by the user which
155 // have been removed again in the problem resolution process, and vice
157 if (repository
.Location() != fVolume
->Location())
160 PackageList
& packagesToActivate
= repository
.PackagesToActivate();
161 PackageList
& packagesToDeactivate
= repository
.PackagesToDeactivate();
163 for (int32 i
= 0; BSolverPackage
* package
= packagesToDeactivate
.ItemAt(i
);
165 if (fPackagesAddedByUser
.erase(package
) == 0)
168 for (SolverPackageMap::iterator it
= fSolverPackages
.begin();
169 it
!= fSolverPackages
.end(); ++it
) {
170 if (it
->second
== package
) {
171 fSolverPackages
.erase(it
);
176 repository
.EnablePackage(package
);
177 packagesToDeactivate
.RemoveItemAt(i
--);
178 packagesToActivate
.RemoveItem(package
);
179 repository
.DeletePackage(package
);
182 for (int32 i
= 0; BSolverPackage
* package
= packagesToActivate
.ItemAt(i
);
184 if (fPackagesRemovedByUser
.erase(package
) == 0)
187 repository
.EnablePackage(package
);
188 packagesToActivate
.RemoveItemAt(i
--);
189 packagesToDeactivate
.RemoveItem(package
);
191 // Note: We keep the package activated, but nonetheless it is gone,
192 // since the user has removed it from the packages directory. So unless
193 // the user moves it back, we won't find it upon next reboot.
194 // TODO: We probable even run into trouble when the user moves in a
195 // replacement afterward.
201 PackageManager::PrepareTransaction(Transaction
& transaction
)
203 Volume
* volume
= fRoot
->GetVolume(transaction
.Repository().Location());
207 return volume
->CreateTransaction(transaction
.Repository().Location(),
208 transaction
.ActivationTransaction(),
209 transaction
.TransactionDirectory());
214 PackageManager::CommitTransaction(Transaction
& transaction
,
215 BCommitTransactionResult
& _result
)
217 Volume
* volume
= fRoot
->GetVolume(transaction
.Repository().Location());
221 // Get the packages that have already been added to/removed from the
222 // packages directory and thus need to be handled specially by
223 // Volume::CommitTransaction().
224 PackageSet packagesAlreadyAdded
;
225 PackageSet packagesAlreadyRemoved
;
226 if (volume
== fVolume
) {
227 const PackageSet
& packagesToActivate
= volume
->PackagesToBeActivated();
228 for (PackageSet::const_iterator it
= packagesToActivate
.begin();
229 it
!= packagesToActivate
.end(); ++it
) {
230 Package
* package
= *it
;
231 if (fPackagesAddedByUser
.find(_SolverPackageFor(package
))
232 != fPackagesAddedByUser
.end()) {
233 packagesAlreadyAdded
.insert(package
);
237 const PackageSet
& packagesToDeactivate
238 = volume
->PackagesToBeDeactivated();
239 for (PackageSet::const_iterator it
= packagesToDeactivate
.begin();
240 it
!= packagesToDeactivate
.end(); ++it
) {
241 Package
* package
= *it
;
242 if (fPackagesRemovedByUser
.find(_SolverPackageFor(package
))
243 != fPackagesRemovedByUser
.end()) {
244 packagesAlreadyRemoved
.insert(package
);
249 volume
->CommitTransaction(transaction
.ActivationTransaction(),
250 packagesAlreadyAdded
, packagesAlreadyRemoved
, _result
);
256 PackageManager::HandleProblems()
260 if (fProblemWindow
== NULL
)
261 fProblemWindow
= new ProblemWindow
;
263 if (!fProblemWindow
->Go(fSolver
, fPackagesAddedByUser
,
264 fPackagesRemovedByUser
)) {
265 throw BAbortedByUserException();
271 PackageManager::ConfirmChanges(bool fromMostSpecific
)
273 // Check whether there are any changes other than those made by the user.
275 ResultWindow
* window
= new ResultWindow
;
276 ObjectDeleter
<ResultWindow
> windowDeleter(window
);
278 bool hasOtherChanges
= false;
279 int32 count
= fInstalledRepositories
.CountItems();
280 if (fromMostSpecific
) {
281 for (int32 i
= count
- 1; i
>= 0; i
--)
283 |= _AddResults(*fInstalledRepositories
.ItemAt(i
), window
);
285 for (int32 i
= 0; i
< count
; i
++)
287 |= _AddResults(*fInstalledRepositories
.ItemAt(i
), window
);
290 if (!hasOtherChanges
)
294 if (windowDeleter
.Detach()->Go() == 0)
295 throw BAbortedByUserException();
300 PackageManager::Warn(status_t error
, const char* format
, ...)
303 va_start(args
, format
);
305 message
.SetToFormatVarArgs(format
, args
);
309 message
<< BString().SetToFormat(": %s", strerror(error
));
311 BNotification
notification(B_ERROR_NOTIFICATION
);
312 notification
.SetGroup(B_TRANSLATE("Package daemon"));
313 notification
.SetTitle(B_TRANSLATE("Warning"));
314 notification
.SetContent(message
);
320 PackageManager::ProgressPackageDownloadStarted(const char* packageName
)
326 PackageManager::ProgressPackageDownloadActive(const char* packageName
,
327 float completionPercentage
, off_t bytes
, off_t totalBytes
)
333 PackageManager::ProgressPackageDownloadComplete(const char* packageName
)
339 PackageManager::ProgressPackageChecksumStarted(const char* title
)
345 PackageManager::ProgressPackageChecksumComplete(const char* title
)
351 PackageManager::ProgressStartApplyingChanges(InstalledRepository
& repository
)
357 PackageManager::ProgressTransactionCommitted(InstalledRepository
& repository
,
358 const BCommitTransactionResult
& result
)
364 PackageManager::ProgressApplyingChangesDone(InstalledRepository
& repository
)
370 PackageManager::JobFailed(BSupportKit::BJob
* job
)
377 PackageManager::JobAborted(BSupportKit::BJob
* job
)
384 PackageManager::_AddResults(InstalledRepository
& repository
,
385 ResultWindow
* window
)
387 if (!repository
.HasChanges())
390 return window
->AddLocationChanges(repository
.Name(),
391 repository
.PackagesToActivate(), fPackagesAddedByUser
,
392 repository
.PackagesToDeactivate(), fPackagesRemovedByUser
);
397 PackageManager::_SolverPackageFor(Package
* package
) const
399 SolverPackageMap::const_iterator it
= fSolverPackages
.find(package
);
400 return it
!= fSolverPackages
.end() ? it
->second
: NULL
;
405 PackageManager::_InitGui()
407 BServer
* server
= dynamic_cast<BServer
*>(be_app
);
408 if (server
== NULL
|| server
->InitGUIContext() != B_OK
)
409 throw BFatalErrorException("failed to initialize the GUI");