package daemon: Implement dependency resolution support

... for package changes performed manually by the user (i.e.
adding/removing files in a packages directory). Currently only done for
the system root. Alternative roots should still work as before, so this
shouldn't affect haikuporter (not tested yet).

Needs some more work (e.g. the GUI part).
This commit is contained in:
Ingo Weinhold
2013-09-10 19:14:21 +02:00
parent 383ac67cfa
commit 38c62dfb8b
10 changed files with 1256 additions and 107 deletions
+11 -9
View File
@@ -4,17 +4,19 @@ UsePrivateSystemHeaders ;
UsePrivateHeaders app kernel shared ; UsePrivateHeaders app kernel shared ;
Server package_daemon Server package_daemon
: :
DebugSupport.cpp DebugSupport.cpp
Job.cpp Job.cpp
JobQueue.cpp JobQueue.cpp
Package.cpp Package.cpp
PackageDaemon.cpp PackageDaemon.cpp
Root.cpp PackageManager.cpp
Volume.cpp ProblemWindow.cpp
Root.cpp
Volume.cpp
: :
be package be package
$(TARGET_LIBSTDC++) $(TARGET_LIBSTDC++)
: :
package_daemon.rdef package_daemon.rdef
; ;
+3
View File
@@ -9,6 +9,8 @@
#define PACKAGE_H #define PACKAGE_H
#include <set>
#include <Entry.h> #include <Entry.h>
#include <Node.h> #include <Node.h>
#include <package/PackageInfo.h> #include <package/PackageInfo.h>
@@ -124,6 +126,7 @@ struct PackageNodeRefHashDefinition {
typedef BOpenHashTable<PackageFileNameHashDefinition> PackageFileNameHashTable; typedef BOpenHashTable<PackageFileNameHashDefinition> PackageFileNameHashTable;
typedef BOpenHashTable<PackageNodeRefHashDefinition> PackageNodeRefHashTable; typedef BOpenHashTable<PackageNodeRefHashDefinition> PackageNodeRefHashTable;
typedef std::set<Package*> PackageSet;
#endif // PACKAGE_H #endif // PACKAGE_H
+450
View File
@@ -0,0 +1,450 @@
/*
* Copyright 2013, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "PackageManager.h"
#include <Alert.h>
#include <Notification.h>
#include <package/DownloadFileRequest.h>
#include <package/RefreshRepositoryRequest.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverPackageSpecifierList.h>
#include <package/solver/SolverProblem.h>
#include <package/solver/SolverProblemSolution.h>
#include <package/manager/Exceptions.h>
#include <package/manager/RepositoryBuilder.h>
#include <Server.h>
#include "ProblemWindow.h"
#include "Root.h"
#include "Volume.h"
using BPackageKit::BManager::BPrivate::BAbortedByUserException;
using BPackageKit::BManager::BPrivate::BFatalErrorException;
using BPackageKit::BManager::BPrivate::BRepositoryBuilder;
PackageManager::PackageManager(Root* root, Volume* volume)
:
BPackageManager(volume->Location()),
BPackageManager::UserInteractionHandler(),
fRoot(root),
fVolume(volume),
fContext(*this, *this),
fSolverPackages(),
fPackagesAddedByUser(),
fPackagesRemovedByUser(),
fProblemWindow(NULL)
{
fInstallationInterface = this;
fRequestHandler = this;
fUserInteractionHandler = this;
}
PackageManager::~PackageManager()
{
if (fProblemWindow != NULL)
fProblemWindow->PostMessage(B_QUIT_REQUESTED);
}
void
PackageManager::HandleUserChanges()
{
const PackageSet& packagesToActivate = fVolume->PackagesToBeActivated();
const PackageSet& packagesToDeactivate = fVolume->PackagesToBeDeactivated();
if (packagesToActivate.empty() && packagesToDeactivate.empty())
return;
if (packagesToActivate.empty()) {
// only packages removed -- use uninstall mode
Init(B_ADD_INSTALLED_REPOSITORIES);
BSolverPackageSpecifierList packagesToUninstall;
for (PackageSet::const_iterator it = packagesToDeactivate.begin();
it != packagesToDeactivate.end(); ++it) {
BSolverPackage* solverPackage = _SolverPackageFor(*it);
if (solverPackage == NULL)
continue;
if (!packagesToUninstall.AppendSpecifier(solverPackage))
throw std::bad_alloc();
fPackagesRemovedByUser.insert(solverPackage);
}
if (fPackagesRemovedByUser.empty())
return;
Uninstall(packagesToUninstall);
} else {
// packages added and (possibly) remove -- manually add/remove those
// from the repository and use verify mode
Init(B_ADD_INSTALLED_REPOSITORIES | B_ADD_REMOTE_REPOSITORIES
| B_REFRESH_REPOSITORIES);
// disable and remove uninstalled packages
InstalledRepository& repository = InstallationRepository();
for (PackageSet::const_iterator it = packagesToDeactivate.begin();
it != packagesToDeactivate.end(); ++it) {
BSolverPackage* solverPackage = _SolverPackageFor(*it);
if (solverPackage == NULL)
continue;
repository.DisablePackage(solverPackage);
if (!repository.PackagesToDeactivate().AddItem(solverPackage))
throw std::bad_alloc();
fPackagesRemovedByUser.insert(solverPackage);
}
// add new packages
BRepositoryBuilder repositoryBuilder(repository);
for (PackageSet::const_iterator it = packagesToActivate.begin();
it != packagesToActivate.end(); ++it) {
Package* package = *it;
BSolverPackage* solverPackage;
repositoryBuilder.AddPackage(package->Info(), NULL, &solverPackage);
fSolverPackages[package] = solverPackage;
if (!repository.PackagesToActivate().AddItem(solverPackage))
throw std::bad_alloc();
fPackagesAddedByUser.insert(solverPackage);
}
if (fPackagesRemovedByUser.empty() && fPackagesAddedByUser.empty())
return;
// TODO: When packages are moved out of the packages directory, we can't create
// a complete "old-state" directory.
VerifyInstallation();
}
}
void
PackageManager::InitInstalledRepository(InstalledRepository& repository)
{
const char* name = repository.InitialName();
BRepositoryBuilder repositoryBuilder(repository, name);
if (Volume* volume = fRoot->GetVolume(repository.Location())) {
for (PackageFileNameHashTable::Iterator it
= volume->PackagesByFileName().GetIterator(); it.HasNext();) {
Package* package = it.Next();
if (package->IsActive()) {
BSolverPackage* solverPackage;
repositoryBuilder.AddPackage(package->Info(), NULL,
&solverPackage);
fSolverPackages[package] = solverPackage;
}
}
}
}
void
PackageManager::ResultComputed(InstalledRepository& repository)
{
// Normalize the result, i.e. remove the packages added by the user which
// have been removed again in the problem resolution process, and vice
// versa.
if (repository.Location() != fVolume->Location())
return;
PackageList& packagesToActivate = repository.PackagesToActivate();
PackageList& packagesToDeactivate = repository.PackagesToDeactivate();
for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
i++) {
if (fPackagesAddedByUser.erase(package) == 0)
continue;
for (SolverPackageMap::iterator it = fSolverPackages.begin();
it != fSolverPackages.end(); ++it) {
if (it->second == package) {
fSolverPackages.erase(it);
break;
}
}
repository.EnablePackage(package);
packagesToDeactivate.RemoveItemAt(i--);
packagesToActivate.RemoveItem(package);
repository.DeletePackage(package);
}
for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
i++) {
if (fPackagesRemovedByUser.erase(package) == 0)
continue;
repository.EnablePackage(package);
packagesToActivate.RemoveItemAt(i--);
packagesToDeactivate.RemoveItem(package);
// Note: We keep the package activated, but nonetheless it is gone,
// since the user has removed it from the packages directory. So unless
// the user moves it back, we won't find it upon next reboot.
// TODO: We probable even run into trouble when the user moves in a
// replacement afterward.
}
}
status_t
PackageManager::PrepareTransaction(Transaction& transaction)
{
Volume* volume = fRoot->GetVolume(transaction.Repository().Location());
if (volume == NULL)
return B_BAD_VALUE;
return volume->CreateTransaction(transaction.Repository().Location(),
transaction.ActivationTransaction(),
transaction.TransactionDirectory());
}
status_t
PackageManager::CommitTransaction(Transaction& transaction,
BDaemonClient::BCommitTransactionResult& _result)
{
debug_printf("PackageManager::CommitTransaction()\n");
Volume* volume = fRoot->GetVolume(transaction.Repository().Location());
if (volume == NULL)
return B_BAD_VALUE;
// Get the packages that have already been added to/removed from the
// packages directory and thus need to be handled specially by
// Volume::CommitTransaction().
PackageSet packagesAlreadyAdded;
PackageSet packagesAlreadyRemoved;
if (volume == fVolume) {
const PackageSet& packagesToActivate = volume->PackagesToBeActivated();
for (PackageSet::const_iterator it = packagesToActivate.begin();
it != packagesToActivate.end(); ++it) {
Package* package = *it;
if (fPackagesAddedByUser.find(_SolverPackageFor(package))
!= fPackagesAddedByUser.end()) {
packagesAlreadyAdded.insert(package);
}
}
const PackageSet& packagesToDeactivate
= volume->PackagesToBeDeactivated();
for (PackageSet::const_iterator it = packagesToDeactivate.begin();
it != packagesToDeactivate.end(); ++it) {
Package* package = *it;
if (fPackagesRemovedByUser.find(_SolverPackageFor(package))
!= fPackagesRemovedByUser.end()) {
packagesAlreadyRemoved.insert(package);
}
}
}
debug_printf(" activating %ld (%zu) packages\n", transaction.ActivationTransaction().PackagesToActivate().CountStrings(), packagesAlreadyAdded.size());
debug_printf(" deactivating %ld (%zu) packages\n", transaction.ActivationTransaction().PackagesToDeactivate().CountStrings(), packagesAlreadyRemoved.size());
volume->CommitTransaction(transaction.ActivationTransaction(),
packagesAlreadyAdded, packagesAlreadyRemoved, _result);
return B_OK;
}
status_t
PackageManager::RefreshRepository(const BRepositoryConfig& repoConfig)
{
return BRefreshRepositoryRequest(fContext, repoConfig).Process();
}
status_t
PackageManager::DownloadPackage(const BString& fileURL,
const BEntry& targetEntry, const BString& checksum)
{
return DownloadFileRequest(fContext, fileURL, targetEntry, checksum)
.Process();
}
void
PackageManager::HandleProblems()
{
_InitGui();
if (fProblemWindow == NULL)
fProblemWindow = new ProblemWindow;
if (!fProblemWindow->Go(fSolver, fPackagesAddedByUser,
fPackagesRemovedByUser)) {
throw BAbortedByUserException();
}
}
void
PackageManager::ConfirmChanges(bool fromMostSpecific)
{
// Check whether there are any changes other than those made by the user.
BString alertText(
"The following additional package changes have to be made:");
bool hasOtherChanges = false;
int32 count = fInstalledRepositories.CountItems();
if (fromMostSpecific) {
for (int32 i = count - 1; i >= 0; i--)
hasOtherChanges
|= _GetResultText(*fInstalledRepositories.ItemAt(i), alertText);
} else {
for (int32 i = 0; i < count; i++)
hasOtherChanges
|= _GetResultText(*fInstalledRepositories.ItemAt(i), alertText);
}
if (!hasOtherChanges)
return;
// show an alert
_InitGui();
BAlert* alert = new BAlert("Package changes", alertText,
"Cancel", "Apply changes");
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
throw BAbortedByUserException();
}
void
PackageManager::Warn(status_t error, const char* format, ...)
{
va_list args;
va_start(args, format);
BString message;
message.SetToFormatVarArgs(format, args);
va_end(args);
if (error != B_OK)
message << BString().SetToFormat(": %s", strerror(error));
BNotification notification(B_ERROR_NOTIFICATION);
notification.SetTitle("Package Daemon");
notification.SetContent(message);
notification.Send();
}
void
PackageManager::ProgressStartApplyingChanges(InstalledRepository& repository)
{
}
void
PackageManager::ProgressTransactionCommitted(InstalledRepository& repository,
const char* transactionDirectoryName)
{
}
void
PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository)
{
}
bool
PackageManager::YesNoDecisionNeeded(const BString& description,
const BString& question, const BString& yes, const BString& no,
const BString& defaultChoice)
{
// ATM we need the BDecisionProvider object for the BContext, but we don't
// execute any request which actually uses it.
return false;
}
void
PackageManager::JobFailed(BJob* job)
{
// TODO:...
}
void
PackageManager::JobAborted(BJob* job)
{
// TODO:...
}
bool
PackageManager::_GetResultText(InstalledRepository& repository,
BString& _text)
{
if (!repository.HasChanges())
return false;
bool hasOtherChanges = false;
bool isTargetLocation
= repository.Location() == fVolume->Location();
BString text = BString().SetToFormat("\n in %s:",
repository.Name().String());
PackageList& packagesToActivate = repository.PackagesToActivate();
PackageList& packagesToDeactivate = repository.PackagesToDeactivate();
for (int32 i = 0; BSolverPackage* package = packagesToActivate.ItemAt(i);
i++) {
if (isTargetLocation
&& fPackagesAddedByUser.find(package)
!= fPackagesAddedByUser.end()) {
continue;
}
text << BString().SetToFormat(
"\n install package %s from repository %s\n",
package->Info().FileName().String(),
package->Repository()->Name().String());
hasOtherChanges = true;
}
for (int32 i = 0; BSolverPackage* package = packagesToDeactivate.ItemAt(i);
i++) {
if (isTargetLocation
&& fPackagesRemovedByUser.find(package)
!= fPackagesRemovedByUser.end()) {
continue;
}
text << BString().SetToFormat(
"\n uninstall package %s\n", package->VersionedName().String());
hasOtherChanges = true;
}
if (!hasOtherChanges)
return false;
_text << text;
return true;
}
BSolverPackage*
PackageManager::_SolverPackageFor(Package* package) const
{
SolverPackageMap::const_iterator it = fSolverPackages.find(package);
return it != fSolverPackages.end() ? it->second : NULL;
}
void
PackageManager::_InitGui()
{
BServer* server = dynamic_cast<BServer*>(be_app);
if (server == NULL || server->InitGUIContext() != B_OK)
throw BFatalErrorException("failed to initialize the GUI");
}
+116
View File
@@ -0,0 +1,116 @@
/*
* Copyright 2013, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_MANAGER_H
#define PACKAGE_MANAGER_H
#include <map>
#include <set>
#include <package/Context.h>
#include <package/Job.h>
#include <package/DaemonClient.h>
#include <package/manager/PackageManager.h>
using BPackageKit::BContext;
using BPackageKit::BDecisionProvider;
using BPackageKit::BJob;
using BPackageKit::BJobStateListener;
using BPackageKit::BPackageInstallationLocation;
using BPackageKit::BRepositoryConfig;
using BPackageKit::BSolverPackage;
using BPackageKit::BPrivate::BDaemonClient;
using BPackageKit::BManager::BPrivate::BPackageManager;
class Package;
class ProblemWindow;
class Root;
class Volume;
class PackageManager : public BPackageManager,
private BPackageManager::InstallationInterface,
private BPackageManager::RequestHandler,
private BPackageManager::UserInteractionHandler,
private BDecisionProvider, private BJobStateListener {
public:
PackageManager(Root* root, Volume* volume);
~PackageManager();
void HandleUserChanges();
private:
// InstallationInterface
virtual void InitInstalledRepository(
InstalledRepository& repository);
virtual void ResultComputed(InstalledRepository& repository);
virtual status_t PrepareTransaction(Transaction& transaction);
virtual status_t CommitTransaction(Transaction& transaction,
BDaemonClient::BCommitTransactionResult&
_result);
private:
// RequestHandler
virtual status_t RefreshRepository(
const BRepositoryConfig& repoConfig);
virtual status_t DownloadPackage(const BString& fileURL,
const BEntry& targetEntry,
const BString& checksum);
private:
// UserInteractionHandler
virtual void HandleProblems();
virtual void ConfirmChanges(bool fromMostSpecific);
virtual void Warn(status_t error, const char* format, ...);
virtual void ProgressStartApplyingChanges(
InstalledRepository& repository);
virtual void ProgressTransactionCommitted(
InstalledRepository& repository,
const char* transactionDirectoryName);
virtual void ProgressApplyingChangesDone(
InstalledRepository& repository);
private:
// BDecisionProvider
virtual bool YesNoDecisionNeeded(const BString& description,
const BString& question,
const BString& yes, const BString& no,
const BString& defaultChoice);
private:
// BJobStateListener
virtual void JobFailed(BJob* job);
virtual void JobAborted(BJob* job);
private:
typedef std::set<BSolverPackage*> SolverPackageSet;
typedef std::map<Package*, BSolverPackage*> SolverPackageMap;
private:
bool _GetResultText(InstalledRepository& repository,
BString& _text);
BSolverPackage* _SolverPackageFor(Package* package) const;
void _InitGui();
private:
Root* fRoot;
Volume* fVolume;
BContext fContext;
SolverPackageMap fSolverPackages;
SolverPackageSet fPackagesAddedByUser;
SolverPackageSet fPackagesRemovedByUser;
ProblemWindow* fProblemWindow;
};
#endif // PACKAGE_MANAGER_H
+287
View File
@@ -0,0 +1,287 @@
/*
* Copyright 2013, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "ProblemWindow.h"
#include <Button.h>
#include <GroupView.h>
#include <LayoutBuilder.h>
#include <RadioButton.h>
#include <StringView.h>
#include <package/solver/Solver.h>
#include <package/solver/SolverPackage.h>
#include <package/solver/SolverProblem.h>
#include <package/solver/SolverProblemSolution.h>
#include <AutoLocker.h>
#include <package/manager/Exceptions.h>
using namespace BPackageKit;
using BPackageKit::BManager::BPrivate::BFatalErrorException;
static const int32 kRetryMessage = 'rtry';
static const int32 kUpdateRetryButtonMessage = 'uprt';
struct ProblemWindow::Solution {
BSolverProblem* fProblem;
const BSolverProblemSolution* fSolution;
Solution()
:
fProblem(NULL),
fSolution(NULL)
{
}
Solution(BSolverProblem* problem, const BSolverProblemSolution* solution)
:
fProblem(problem),
fSolution(solution)
{
}
};
ProblemWindow::ProblemWindow()
:
BWindow(BRect(0, 0, 400, 300), "Package problems", B_TITLED_WINDOW_LOOK,
B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS | B_NOT_MINIMIZABLE | B_AUTO_UPDATE_SIZE_LIMITS,
B_ALL_WORKSPACES),
fDoneSemaphore(-1),
fClientWaiting(false),
fAccepted(false),
fContainerView(NULL),
fCancelButton(NULL),
fRetryButton(NULL),
fSolutions(),
fPackagesAddedByUser(NULL),
fPackagesRemovedByUser(NULL)
{
fDoneSemaphore = create_sem(0, "package problems");
if (fDoneSemaphore < 0)
throw std::bad_alloc();
BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING)
.SetInsets(B_USE_SMALL_INSETS)
.Add(new BStringView(NULL,
"The following problems have been encountered. Please select a "
"solution for each:"))
// TODO: Use a scroll view!
.Add(fContainerView = new BGroupView(B_VERTICAL, 0))
.AddGroup(B_HORIZONTAL)
.Add(fCancelButton = new BButton("Cancel", new BMessage(B_CANCEL)))
.AddGlue()
.Add(fRetryButton = new BButton("Retry",
new BMessage(kRetryMessage)))
.End();
}
ProblemWindow::~ProblemWindow()
{
if (fDoneSemaphore >= 0)
delete_sem(fDoneSemaphore);
}
bool
ProblemWindow::Go(BSolver* solver, const SolverPackageSet& packagesAddedByUser,
const SolverPackageSet& packagesRemovedByUser)
{
AutoLocker<ProblemWindow> locker(this);
fPackagesAddedByUser = &packagesAddedByUser;
fPackagesRemovedByUser = &packagesRemovedByUser;
_ClearProblemsGui();
_AddProblemsGui(solver);
fCancelButton->SetEnabled(true);
fRetryButton->SetEnabled(false);
if (IsHidden()) {
CenterOnScreen();
Show();
}
fAccepted = false;
fClientWaiting = true;
locker.Unlock();
while (acquire_sem(fDoneSemaphore) == B_INTERRUPTED) {
}
locker.Lock();
if (!locker.IsLocked() || !fAccepted || !_AnySolutionSelected())
return false;
// set the solutions
for (SolutionMap::const_iterator it = fSolutions.begin();
it != fSolutions.end(); ++it) {
BRadioButton* button = it->first;
if (button->Value() == B_CONTROL_ON) {
const Solution& solution = it->second;
status_t error = solver->SelectProblemSolution(solution.fProblem,
solution.fSolution);
if (error != B_OK)
throw BFatalErrorException(error, "failed to set solution");
}
}
return true;
}
bool
ProblemWindow::QuitRequested()
{
if (fClientWaiting) {
fClientWaiting = false;
release_sem(fDoneSemaphore);
}
return true;
}
void
ProblemWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_CANCEL:
Hide();
fClientWaiting = false;
release_sem(fDoneSemaphore);
break;
case kRetryMessage:
fCancelButton->SetEnabled(false);
fRetryButton->SetEnabled(false);
fAccepted = true;
fClientWaiting = false;
release_sem(fDoneSemaphore);
break;
case kUpdateRetryButtonMessage:
fRetryButton->SetEnabled(_AnySolutionSelected());
break;
default:
BWindow::MessageReceived(message);
break;
}
}
void
ProblemWindow::_ClearProblemsGui()
{
fSolutions.clear();
int32 count = fContainerView->CountChildren();
for (int32 i = count - 1; i >= 0; i--) {
BView* child = fContainerView->ChildAt(i);
fContainerView->RemoveChild(child);
delete child;
}
}
void
ProblemWindow::_AddProblemsGui(BSolver* solver)
{
rgb_color evenBackground = ui_color(B_LIST_BACKGROUND_COLOR);
rgb_color oddBackground = tint_color(evenBackground, 1.04);
int32 problemCount = solver->CountProblems();
for (int32 i = 0; i < problemCount; i++) {
_AddProblem(solver->ProblemAt(i),
(i & 1) == 0 ? evenBackground : oddBackground);
}
}
void
ProblemWindow::_AddProblem(BSolverProblem* problem,
const rgb_color& backgroundColor)
{
BGroupView* problemGroup = new BGroupView(B_VERTICAL);
fContainerView->AddChild(problemGroup);
problemGroup->GroupLayout()->SetInsets(B_USE_SMALL_INSETS);
problemGroup->SetViewColor(backgroundColor);
BStringView* problemView = new BStringView(NULL, problem->ToString());
problemGroup->AddChild(problemView);
BFont problemFont;
problemView->GetFont(&problemFont);
problemFont.SetFace(B_BOLD_FACE);
problemView->SetFont(&problemFont);
int32 solutionCount = problem->CountSolutions();
for (int32 k = 0; k < solutionCount; k++) {
const BSolverProblemSolution* solution = problem->SolutionAt(k);
BRadioButton* solutionButton = new BRadioButton(
BString().SetToFormat("solution %" B_PRId32 ":", k + 1),
new BMessage(kUpdateRetryButtonMessage));
problemGroup->AddChild(solutionButton);
BGroupLayout* elementsGroup = new BGroupLayout(B_VERTICAL);
problemGroup->AddChild(elementsGroup);
elementsGroup->SetInsets(20, 0, 0, 0);
int32 elementCount = solution->CountElements();
for (int32 l = 0; l < elementCount; l++) {
const BSolverProblemSolutionElement* element
= solution->ElementAt(l);
BStringView* elementView = new BStringView(NULL,
BString().SetToFormat("- %s",
_SolutionElementText(element).String()));
elementsGroup->AddView(elementView);
}
fSolutions[solutionButton] = Solution(problem, solution);
}
BRadioButton* ignoreButton = new BRadioButton("ignore problem for now",
new BMessage(kUpdateRetryButtonMessage));
problemGroup->AddChild(ignoreButton);
ignoreButton->SetValue(B_CONTROL_ON);
}
BString
ProblemWindow::_SolutionElementText(
const BSolverProblemSolutionElement* element) const
{
// Reword text for B_ALLOW_DEINSTALLATION, if the package has been added
// by the user.
BSolverPackage* package = element->SourcePackage();
if (element->Type() == BSolverProblemSolutionElement::B_ALLOW_DEINSTALLATION
&& package != NULL
&& fPackagesAddedByUser->find(package) != fPackagesAddedByUser->end()) {
return BString("don't activate package %source%").ReplaceAll(
"%source%", package->VersionedName());
}
return element->ToString();
}
bool
ProblemWindow::_AnySolutionSelected() const
{
for (SolutionMap::const_iterator it = fSolutions.begin();
it != fSolutions.end(); ++it) {
BRadioButton* button = it->first;
if (button->Value() == B_CONTROL_ON)
return true;
}
return false;
}
+78
View File
@@ -0,0 +1,78 @@
/*
* Copyright 2013, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef PROBLEM_WINDOW_H
#define PROBLEM_WINDOW_H
#include <map>
#include <set>
#include <Window.h>
namespace BPackageKit {
class BSolver;
class BSolverPackage;
class BSolverProblem;
class BSolverProblemSolution;
class BSolverProblemSolutionElement;
}
using BPackageKit::BSolver;
using BPackageKit::BSolverPackage;
using BPackageKit::BSolverProblem;
using BPackageKit::BSolverProblemSolution;
using BPackageKit::BSolverProblemSolutionElement;
class BButton;
class BGroupView;
class BRadioButton;
class ProblemWindow : public BWindow {
public:
typedef std::set<BSolverPackage*> SolverPackageSet;
public:
ProblemWindow();
virtual ~ProblemWindow();
bool Go(BSolver* solver,
const SolverPackageSet& packagesAddedByUser,
const SolverPackageSet&
packagesRemovedByUser);
virtual bool QuitRequested();
virtual void MessageReceived(BMessage* message);
private:
struct Solution;
typedef std::map<BRadioButton*, Solution> SolutionMap;
private:
void _ClearProblemsGui();
void _AddProblemsGui(BSolver* solver);
void _AddProblem(BSolverProblem* problem,
const rgb_color& backgroundColor);
BString _SolutionElementText(
const BSolverProblemSolutionElement*
element) const;
bool _AnySolutionSelected() const;
private:
sem_id fDoneSemaphore;
bool fClientWaiting;
bool fAccepted;
BGroupView* fContainerView;
BButton* fCancelButton;
BButton* fRetryButton;
SolutionMap fSolutions;
const SolverPackageSet* fPackagesAddedByUser;
const SolverPackageSet* fPackagesRemovedByUser;
};
#endif // PROBLEM_WINDOW_H
+72 -18
View File
@@ -9,6 +9,7 @@
#include "Root.h" #include "Root.h"
#include <Alert.h>
#include <Directory.h> #include <Directory.h>
#include <Entry.h> #include <Entry.h>
#include <package/PackageDefs.h> #include <package/PackageDefs.h>
@@ -16,13 +17,17 @@
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <AutoLocker.h> #include <AutoLocker.h>
#include <Server.h>
#include <package/DaemonDefs.h> #include <package/DaemonDefs.h>
#include <package/manager/Exceptions.h>
#include "DebugSupport.h" #include "DebugSupport.h"
#include "PackageManager.h"
using namespace BPackageKit::BPrivate; using namespace BPackageKit::BPrivate;
using namespace BPackageKit::BManager::BPrivate;
// #pragma mark - VolumeJob // #pragma mark - VolumeJob
@@ -215,6 +220,22 @@ Root::FindVolume(dev_t deviceID) const
} }
Volume*
Root::GetVolume(BPackageInstallationLocation location)
{
switch ((BPackageInstallationLocation)location) {
case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM:
return fSystemVolume;
case B_PACKAGE_INSTALLATION_LOCATION_COMMON:
return fCommonVolume;
case B_PACKAGE_INSTALLATION_LOCATION_HOME:
return fHomeVolume;
default:
return NULL;
}
}
void void
Root::HandleRequest(BMessage* message) Root::HandleRequest(BMessage* message)
{ {
@@ -259,22 +280,6 @@ Root::_GetVolume(PackageFSMountType mountType)
} }
Volume*
Root::_GetVolume(BPackageInstallationLocation location)
{
switch ((BPackageInstallationLocation)location) {
case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM:
return fSystemVolume;
case B_PACKAGE_INSTALLATION_LOCATION_COMMON:
return fCommonVolume;
case B_PACKAGE_INSTALLATION_LOCATION_HOME:
return fHomeVolume;
default:
return NULL;
}
}
Volume* Volume*
Root::_NextVolumeFor(Volume* volume) Root::_NextVolumeFor(Volume* volume)
{ {
@@ -330,8 +335,39 @@ Root::_ProcessNodeMonitorEvents(Volume* volume)
{ {
volume->ProcessPendingNodeMonitorEvents(); volume->ProcessPendingNodeMonitorEvents();
if (volume->HasPendingPackageActivationChanges()) if (!volume->HasPendingPackageActivationChanges())
return;
// If this is not the system root, just activate/deactivate the packages.
if (!fIsSystemRoot) {
volume->ProcessPendingPackageActivationChanges(); volume->ProcessPendingPackageActivationChanges();
return;
}
// For the system root do the full dependency analysis.
PRINT("Root::_ProcessNodeMonitorEvents(): running package manager...\n");
try {
PackageManager packageManager(this, volume);
packageManager.HandleUserChanges();
} catch (BNothingToDoException&) {
PRINT("Root::_ProcessNodeMonitorEvents(): -> nothing to do\n");
} catch (std::bad_alloc&) {
_ShowError(
"Insufficient memory while trying to apply package changes.");
} catch (BFatalErrorException& exception) {
if (exception.Error() == B_OK) {
_ShowError(exception.Message());
} else {
_ShowError(BString().SetToFormat("%s: %s",
exception.Message().String(), strerror(exception.Error())));
}
// TODO: Print exception.Details()?
} catch (BAbortedByUserException&) {
PRINT("Root::_ProcessNodeMonitorEvents(): -> aborted by user\n");
}
volume->ClearPackageActivationChanges();
} }
@@ -347,7 +383,7 @@ Root::_HandleRequest(BMessage* message)
// get the volume and let it handle the message // get the volume and let it handle the message
AutoLocker<BLocker> locker(fLock); AutoLocker<BLocker> locker(fLock);
Volume* volume = _GetVolume((BPackageInstallationLocation)location); Volume* volume = GetVolume((BPackageInstallationLocation)location);
locker.Unlock(); locker.Unlock();
if (volume != NULL) { if (volume != NULL) {
@@ -396,3 +432,21 @@ Root::_JobRunner()
return B_OK; return B_OK;
} }
/*static*/ void
Root::_ShowError(const char* errorMessage)
{
BServer* server = dynamic_cast<BServer*>(be_app);
if (server != NULL && server->InitGUIContext() == B_OK) {
BAlert* alert = new(std::nothrow) BAlert("Package error",
errorMessage, "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
if (alert != NULL) {
alert->SetShortcut(0, B_ESCAPE);
alert->Go();
return;
}
}
ERROR("Root::_ShowError(): %s\n", errorMessage);
}
+4 -2
View File
@@ -42,6 +42,8 @@ public:
// deletes the volume (eventually) // deletes the volume (eventually)
Volume* FindVolume(dev_t deviceID) const; Volume* FindVolume(dev_t deviceID) const;
Volume* GetVolume(
BPackageInstallationLocation location);
void HandleRequest(BMessage* message); void HandleRequest(BMessage* message);
@@ -60,8 +62,6 @@ private:
private: private:
Volume** _GetVolume(PackageFSMountType mountType); Volume** _GetVolume(PackageFSMountType mountType);
Volume* _GetVolume(
BPackageInstallationLocation location);
Volume* _NextVolumeFor(Volume* volume); Volume* _NextVolumeFor(Volume* volume);
void _InitPackages(Volume* volume); void _InitPackages(Volume* volume);
@@ -74,6 +74,8 @@ private:
static status_t _JobRunnerEntry(void* data); static status_t _JobRunnerEntry(void* data);
status_t _JobRunner(); status_t _JobRunner();
static void _ShowError(const char* errorMessage);
private: private:
mutable BLocker fLock; mutable BLocker fLock;
node_ref fNodeRef; node_ref fNodeRef;
+203 -75
View File
@@ -249,30 +249,47 @@ private:
struct Volume::CommitTransactionHandler { struct Volume::CommitTransactionHandler {
CommitTransactionHandler(Volume* volume, BMessage* request, BMessage& reply) CommitTransactionHandler(Volume* volume,
const PackageSet& packagesAlreadyAdded,
const PackageSet& packagesAlreadyRemoved)
: :
fVolume(volume), fVolume(volume),
fRequest(request),
fReply(reply),
fPackagesToActivate(20, true), fPackagesToActivate(20, true),
fPackagesToDeactivate(), fPackagesToDeactivate(),
fAddedPackages(), fAddedPackages(),
fRemovedPackages() fRemovedPackages(),
fPackagesAlreadyAdded(packagesAlreadyAdded),
fPackagesAlreadyRemoved(packagesAlreadyRemoved)
{ {
} }
void HandleRequest() void HandleRequest(BMessage* request, BMessage* reply)
{
status_t error;
BActivationTransaction transaction(request, &error);
if (error == B_OK)
error = transaction.InitCheck();
if (error != B_OK) {
if (error == B_NO_MEMORY)
throw Exception(B_NO_MEMORY);
throw Exception(B_DAEMON_BAD_REQUEST);
}
HandleRequest(transaction, reply);
}
void HandleRequest(const BActivationTransaction& transaction,
BMessage* reply)
{ {
// check the change count // check the change count
int64 changeCount; if (transaction.ChangeCount() != fVolume->fChangeCount)
if (fRequest->FindInt64("change count", &changeCount) != B_OK)
throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH); throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH);
// collect the packages to deactivate // collect the packages to deactivate
_GetPackagesToDeactivate(); _GetPackagesToDeactivate(transaction);
// read the packages to activate // read the packages to activate
_ReadPackagesToActivate(); _ReadPackagesToActivate(transaction);
// anything to do at all? // anything to do at all?
if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) { if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) {
@@ -281,7 +298,7 @@ struct Volume::CommitTransactionHandler {
} }
// create an old state directory // create an old state directory
_CreateOldStateDirectory(); _CreateOldStateDirectory(reply);
// move packages to deactivate to old state directory // move packages to deactivate to old state directory
_RemovePackagesToDeactivate(); _RemovePackagesToDeactivate();
@@ -311,29 +328,25 @@ struct Volume::CommitTransactionHandler {
_RemoveOldStateDirectory(); _RemoveOldStateDirectory();
} }
const BString& OldStateDirectoryName() const
{
return fOldStateDirectoryName;
}
private: private:
typedef BObjectList<Package> PackageList; typedef BObjectList<Package> PackageList;
void _GetPackagesToDeactivate() void _GetPackagesToDeactivate(const BActivationTransaction& transaction)
{ {
static const char* const kPackagesToDeactivateFieldName = "deactivate"; // get the number of packages to deactivate
const BStringList& packagesToDeactivate
// get the number of packages to activate = transaction.PackagesToDeactivate();
type_code type; int32 packagesToDeactivateCount = packagesToDeactivate.CountStrings();
int32 packagesToDeactivateCount; if (packagesToDeactivateCount == 0)
if (fRequest->GetInfo(kPackagesToDeactivateFieldName, &type,
&packagesToDeactivateCount) != B_OK) {
// the field is missing, i.e. no packages shall be deactivated
return; return;
}
for (int32 i = 0; i < packagesToDeactivateCount; i++) { for (int32 i = 0; i < packagesToDeactivateCount; i++) {
const char* packageName; BString packageName = packagesToDeactivate.StringAt(i);
status_t error = fRequest->FindString(
kPackagesToDeactivateFieldName, i, &packageName);
if (error != B_OK)
throw Exception(error);
Package* package = fVolume->fPackagesByFileName.Lookup(packageName); Package* package = fVolume->fPackagesByFileName.Lookup(packageName);
if (package == NULL) { if (package == NULL) {
throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package", throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package",
@@ -346,30 +359,19 @@ private:
} }
} }
void _ReadPackagesToActivate() void _ReadPackagesToActivate(const BActivationTransaction& transaction)
{ {
static const char* const kPackagesToActivateFieldName = "activate";
// get the number of packages to activate // get the number of packages to activate
type_code type; const BStringList& packagesToActivate
int32 packagesToActivateCount; = transaction.PackagesToActivate();
if (fRequest->GetInfo(kPackagesToActivateFieldName, &type, int32 packagesToActivateCount = packagesToActivate.CountStrings();
&packagesToActivateCount) != B_OK) { if (packagesToActivateCount == 0)
// the field is missing, i.e. no packages shall be activated
return; return;
}
// get the name of the transaction directory // check the transaction directory name -- we only allow a simple
BString transactionDirectoryName; // subdirectory of the admin directory
if (packagesToActivateCount > 0) { const BString& transactionDirectoryName
if (fRequest->FindString("transaction", &transactionDirectoryName) = transaction.TransactionDirectoryName();
!= B_OK) {
throw Exception(B_DAEMON_BAD_REQUEST);
}
}
// check the name -- we only allow a simple subdirectory of the admin
// directory
if (transactionDirectoryName.IsEmpty() if (transactionDirectoryName.IsEmpty()
|| transactionDirectoryName.FindFirst('/') >= 0 || transactionDirectoryName.FindFirst('/') >= 0
|| transactionDirectoryName == "." || transactionDirectoryName == "."
@@ -394,19 +396,23 @@ private:
// read the packages // read the packages
for (int32 i = 0; i < packagesToActivateCount; i++) { for (int32 i = 0; i < packagesToActivateCount; i++) {
const char* packageName; BString packageName = packagesToActivate.StringAt(i);
error = fRequest->FindString(kPackagesToActivateFieldName, i,
&packageName);
if (error != B_OK)
throw Exception(error);
// make sure it doesn't clash with an already existing package // make sure it doesn't clash with an already existing package
Package* package = fVolume->fPackagesByFileName.Lookup(packageName); Package* package = fVolume->fPackagesByFileName.Lookup(packageName);
if (package != NULL if (package != NULL) {
&& fPackagesToDeactivate.find(package) if (fPackagesAlreadyAdded.find(package)
== fPackagesToDeactivate.end()) { != fPackagesAlreadyAdded.end()) {
throw Exception(B_DAEMON_PACKAGE_ALREADY_EXISTS, NULL, if (!fPackagesToActivate.AddItem(package))
packageName); throw Exception(B_NO_MEMORY);
continue;
}
if (fPackagesToDeactivate.find(package)
== fPackagesToDeactivate.end()) {
throw Exception(B_DAEMON_PACKAGE_ALREADY_EXISTS, NULL,
packageName);
}
} }
// read the package // read the package
@@ -430,7 +436,7 @@ private:
} }
} }
void _CreateOldStateDirectory() void _CreateOldStateDirectory(BMessage* reply)
{ {
// construct a nice name from the current date and time // construct a nice name from the current date and time
time_t nowSeconds = time(NULL); time_t nowSeconds = time(NULL);
@@ -478,9 +484,11 @@ private:
throw Exception(error, "failed to write old activation file"); throw Exception(error, "failed to write old activation file");
// add the old state directory to the reply // add the old state directory to the reply
error = fReply.AddString("old state", fOldStateDirectoryName); if (reply != NULL) {
if (error != B_OK) error = reply->AddString("old state", fOldStateDirectoryName);
throw Exception(error, "failed to add field to reply"); if (error != B_OK)
throw Exception(error, "failed to add field to reply");
}
} }
void _RemovePackagesToDeactivate() void _RemovePackagesToDeactivate()
@@ -490,8 +498,14 @@ private:
for (PackageSet::const_iterator it = fPackagesToDeactivate.begin(); for (PackageSet::const_iterator it = fPackagesToDeactivate.begin();
it != fPackagesToDeactivate.end(); ++it) { it != fPackagesToDeactivate.end(); ++it) {
// get an BEntry for the package
Package* package = *it; Package* package = *it;
if (fPackagesAlreadyRemoved.find(package)
!= fPackagesAlreadyRemoved.end()) {
fRemovedPackages.insert(package);
continue;
}
// get a BEntry for the package
entry_ref entryRef; entry_ref entryRef;
entryRef.device = fVolume->fPackagesDirectoryRef.device; entryRef.device = fVolume->fPackagesDirectoryRef.device;
entryRef.directory = fVolume->fPackagesDirectoryRef.node; entryRef.directory = fVolume->fPackagesDirectoryRef.node;
@@ -532,8 +546,14 @@ private:
int32 count = fPackagesToActivate.CountItems(); int32 count = fPackagesToActivate.CountItems();
for (int32 i = 0; i < count; i++) { for (int32 i = 0; i < count; i++) {
// get an BEntry for the package
Package* package = fPackagesToActivate.ItemAt(i); Package* package = fPackagesToActivate.ItemAt(i);
if (fPackagesAlreadyAdded.find(package)
!= fPackagesAlreadyAdded.end()) {
fAddedPackages.insert(package);
continue;
}
// get a BEntry for the package
entry_ref entryRef; entry_ref entryRef;
entryRef.device = fTransactionDirectoryRef.device; entryRef.device = fTransactionDirectoryRef.device;
entryRef.directory = fTransactionDirectoryRef.node; entryRef.directory = fTransactionDirectoryRef.node;
@@ -582,6 +602,11 @@ private:
Package* package = *it; Package* package = *it;
fVolume->_RemovePackage(package); fVolume->_RemovePackage(package);
if (fPackagesAlreadyAdded.find(package)
!= fPackagesAlreadyAdded.end()) {
continue;
}
if (transactionDirectory.InitCheck() != B_OK) if (transactionDirectory.InitCheck() != B_OK)
continue; continue;
@@ -631,8 +656,13 @@ private:
for (PackageSet::iterator it = fRemovedPackages.begin(); for (PackageSet::iterator it = fRemovedPackages.begin();
it != fRemovedPackages.end(); ++it) { it != fRemovedPackages.end(); ++it) {
// get an BEntry for the package
Package* package = *it; Package* package = *it;
if (fPackagesAlreadyRemoved.find(package)
!= fPackagesAlreadyRemoved.end()) {
continue;
}
// get a BEntry for the package
BEntry entry; BEntry entry;
status_t error = entry.SetTo(&fOldStateDirectory, status_t error = entry.SetTo(&fOldStateDirectory,
package->FileName()); package->FileName());
@@ -681,16 +711,16 @@ private:
} }
private: private:
Volume* fVolume; Volume* fVolume;
BMessage* fRequest; PackageList fPackagesToActivate;
BMessage& fReply; PackageSet fPackagesToDeactivate;
PackageList fPackagesToActivate; PackageSet fAddedPackages;
PackageSet fPackagesToDeactivate; PackageSet fRemovedPackages;
PackageSet fAddedPackages; const PackageSet& fPackagesAlreadyAdded;
PackageSet fRemovedPackages; const PackageSet& fPackagesAlreadyRemoved;
BDirectory fOldStateDirectory; BDirectory fOldStateDirectory;
BString fOldStateDirectoryName; BString fOldStateDirectoryName;
node_ref fTransactionDirectoryRef; node_ref fTransactionDirectoryRef;
}; };
@@ -710,6 +740,7 @@ Volume::Volume(BLooper* looper)
fPackagesByNodeRef(), fPackagesByNodeRef(),
fPendingNodeMonitorEventsLock("pending node monitor events"), fPendingNodeMonitorEventsLock("pending node monitor events"),
fPendingNodeMonitorEvents(), fPendingNodeMonitorEvents(),
fNodeMonitorEventHandleTime(0),
fPackagesToBeActivated(), fPackagesToBeActivated(),
fPackagesToBeDeactivated(), fPackagesToBeDeactivated(),
fChangeCount(0), fChangeCount(0),
@@ -1007,10 +1038,10 @@ Volume::HandleCommitTransactionRequest(BMessage* message)
return; return;
// perform the request // perform the request
CommitTransactionHandler handler(this, message, reply); CommitTransactionHandler handler(this, PackageSet(), PackageSet());
int32 error; int32 error;
try { try {
handler.HandleRequest(); handler.HandleRequest(message, &reply);
error = B_DAEMON_OK; error = B_DAEMON_OK;
} catch (Exception& exception) { } catch (Exception& exception) {
error = exception.Error(); error = exception.Error();
@@ -1086,6 +1117,23 @@ Volume::MessageReceived(BMessage* message)
} }
BPackageInstallationLocation
Volume::Location() const
{
switch (fMountType) {
case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM;
case PACKAGE_FS_MOUNT_TYPE_COMMON:
return B_PACKAGE_INSTALLATION_LOCATION_COMMON;
case PACKAGE_FS_MOUNT_TYPE_HOME:
return B_PACKAGE_INSTALLATION_LOCATION_HOME;
case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
default:
return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT;
}
}
int int
Volume::OpenRootDirectory() const Volume::OpenRootDirectory() const
{ {
@@ -1150,6 +1198,86 @@ Volume::ProcessPendingPackageActivationChanges()
} }
void
Volume::ClearPackageActivationChanges()
{
fPackagesToBeActivated.clear();
fPackagesToBeDeactivated.clear();
}
status_t
Volume::CreateTransaction(BPackageInstallationLocation location,
BActivationTransaction& _transaction, BDirectory& _transactionDirectory)
{
// open admin directory
BDirectory adminDirectory;
status_t error = _OpenPackagesSubDirectory(
RelativePath(kAdminDirectoryName), true, adminDirectory);
if (error != B_OK)
return error;
// create a transaction directory
int uniqueId = 1;
BString directoryName;
for (;; uniqueId++) {
directoryName.SetToFormat("transaction-%d", uniqueId);
if (directoryName.IsEmpty())
return B_NO_MEMORY;
error = adminDirectory.CreateDirectory(directoryName,
&_transactionDirectory);
if (error == B_OK)
break;
if (error != B_FILE_EXISTS)
return error;
}
// init the transaction
error = _transaction.SetTo(location, fChangeCount, directoryName);
if (error != B_OK) {
BEntry entry;
_transactionDirectory.GetEntry(&entry);
_transactionDirectory.Unset();
if (entry.InitCheck() == B_OK)
entry.Remove();
return error;
}
return B_OK;
}
void
Volume::CommitTransaction(const BActivationTransaction& transaction,
const PackageSet& packagesAlreadyAdded,
const PackageSet& packagesAlreadyRemoved,
BDaemonClient::BCommitTransactionResult& _result)
{
// perform the request
CommitTransactionHandler handler(this, packagesAlreadyAdded,
packagesAlreadyRemoved);
int32 error;
try {
handler.HandleRequest(transaction, NULL);
error = B_DAEMON_OK;
_result.SetTo(error, BString(), BString(),
handler.OldStateDirectoryName());
} catch (Exception& exception) {
error = exception.Error();
_result.SetTo(error, exception.ErrorMessage(), exception.PackageName(),
BString());
} catch (std::bad_alloc& exception) {
error = B_NO_MEMORY;
_result.SetTo(error, BString(), BString(), BString());
}
// revert on error
if (error != B_DAEMON_OK)
handler.Revert();
}
void void
Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created) Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created)
{ {
+32 -3
View File
@@ -9,19 +9,22 @@
#define VOLUME_H #define VOLUME_H
#include <set>
#include <Handler.h> #include <Handler.h>
#include <Locker.h> #include <Locker.h>
#include <Message.h> #include <Message.h>
#include <String.h> #include <String.h>
#include <package/ActivationTransaction.h>
#include <package/DaemonClient.h>
#include <package/packagefs.h> #include <package/packagefs.h>
#include <util/DoublyLinkedList.h> #include <util/DoublyLinkedList.h>
#include "Package.h" #include "Package.h"
using BPackageKit::BPrivate::BActivationTransaction;
using BPackageKit::BPrivate::BDaemonClient;
class BDirectory; class BDirectory;
class Root; class Root;
@@ -31,6 +34,10 @@ namespace BPackageKit {
class BSolverRepository; class BSolverRepository;
} }
using BPackageKit::BPackageInstallationLocation;
using BPackageKit::BSolver;
using BPackageKit::BSolverRepository;
class Volume : public BHandler { class Volume : public BHandler {
public: public:
@@ -61,6 +68,7 @@ public:
{ return fPath; } { return fPath; }
PackageFSMountType MountType() const PackageFSMountType MountType() const
{ return fMountType; } { return fMountType; }
BPackageInstallationLocation Location() const;
const node_ref& RootDirectoryRef() const const node_ref& RootDirectoryRef() const
{ return fRootDirectoryRef; } { return fRootDirectoryRef; }
@@ -81,12 +89,33 @@ public:
void SetRoot(Root* root) void SetRoot(Root* root)
{ fRoot = root; } { fRoot = root; }
const PackageFileNameHashTable& PackagesByFileName() const
{ return fPackagesByFileName; }
const PackageNodeRefHashTable& PackagesByNodeRef() const
{ return fPackagesByNodeRef; }
int OpenRootDirectory() const; int OpenRootDirectory() const;
void ProcessPendingNodeMonitorEvents(); void ProcessPendingNodeMonitorEvents();
bool HasPendingPackageActivationChanges() const; bool HasPendingPackageActivationChanges() const;
void ProcessPendingPackageActivationChanges(); void ProcessPendingPackageActivationChanges();
void ClearPackageActivationChanges();
const PackageSet& PackagesToBeActivated() const
{ return fPackagesToBeActivated; }
const PackageSet& PackagesToBeDeactivated() const
{ return fPackagesToBeDeactivated; }
status_t CreateTransaction(
BPackageInstallationLocation location,
BActivationTransaction& _transaction,
BDirectory& _transactionDirectory);
void CommitTransaction(
const BActivationTransaction& transaction,
const PackageSet& packagesAlreadyAdded,
const PackageSet& packagesAlreadyRemoved,
BDaemonClient::BCommitTransactionResult&
_result);
private: private:
struct NodeMonitorEvent; struct NodeMonitorEvent;
@@ -97,7 +126,6 @@ private:
friend struct CommitTransactionHandler; friend struct CommitTransactionHandler;
typedef DoublyLinkedList<NodeMonitorEvent> NodeMonitorEventList; typedef DoublyLinkedList<NodeMonitorEvent> NodeMonitorEventList;
typedef std::set<Package*> PackageSet;
private: private:
void _HandleEntryCreatedOrRemoved( void _HandleEntryCreatedOrRemoved(
@@ -164,6 +192,7 @@ private:
PackageNodeRefHashTable fPackagesByNodeRef; PackageNodeRefHashTable fPackagesByNodeRef;
BLocker fPendingNodeMonitorEventsLock; BLocker fPendingNodeMonitorEventsLock;
NodeMonitorEventList fPendingNodeMonitorEvents; NodeMonitorEventList fPendingNodeMonitorEvents;
bigtime_t fNodeMonitorEventHandleTime;
PackageSet fPackagesToBeActivated; PackageSet fPackagesToBeActivated;
PackageSet fPackagesToBeDeactivated; PackageSet fPackagesToBeDeactivated;
int64 fChangeCount; int64 fChangeCount;