2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
13 #include <Directory.h>
15 #include <package/CommitTransactionResult.h>
16 #include <package/PackageDefs.h>
19 #include <AutoDeleter.h>
20 #include <AutoLocker.h>
23 #include <package/DaemonDefs.h>
24 #include <package/manager/Exceptions.h>
26 #include "Constants.h"
27 #include "DebugSupport.h"
28 #include "PackageManager.h"
31 using namespace BPackageKit::BPrivate
;
32 using namespace BPackageKit::BManager::BPrivate
;
35 // #pragma mark - AbstractVolumeJob
38 struct Root::AbstractVolumeJob
: public Job
{
39 AbstractVolumeJob(Volume
* volume
)
45 Volume
* GetVolume() const
55 // #pragma mark - VolumeJob
58 struct Root::VolumeJob
: public AbstractVolumeJob
{
59 VolumeJob(Volume
* volume
, void (Root::*method
)(Volume
*))
61 AbstractVolumeJob(volume
),
68 (fVolume
->GetRoot()->*fMethod
)(fVolume
);
72 void (Root::*fMethod
)(Volume
*);
76 // #pragma mark - ProcessNodeMonitorEventsJob
79 struct Root::ProcessNodeMonitorEventsJob
: public VolumeJob
{
80 ProcessNodeMonitorEventsJob(Volume
* volume
, void (Root::*method
)(Volume
*))
82 VolumeJob(volume
, method
)
84 fVolume
->PackageJobPending();
87 ~ProcessNodeMonitorEventsJob()
89 fVolume
->PackageJobFinished();
94 // #pragma mark - CommitTransactionJob
97 struct Root::CommitTransactionJob
: public AbstractVolumeJob
{
98 CommitTransactionJob(Root
* root
, Volume
* volume
, BMessage
* message
)
100 AbstractVolumeJob(volume
),
104 fVolume
->PackageJobPending();
107 ~CommitTransactionJob()
109 fVolume
->PackageJobFinished();
114 fRoot
->_CommitTransaction(fVolume
, fMessage
.Get());
119 ObjectDeleter
<BMessage
> fMessage
;
123 // #pragma mark - VolumeJobFilter
126 struct Root::VolumeJobFilter
: public ::JobQueue::Filter
{
127 VolumeJobFilter(Volume
* volume
)
133 virtual bool FilterJob(Job
* job
)
135 AbstractVolumeJob
* volumeJob
= dynamic_cast<AbstractVolumeJob
*>(job
);
136 return volumeJob
!= NULL
&& volumeJob
->GetVolume() == fVolume
;
144 // #pragma mark - Root
149 fLock("packagefs root"),
151 fIsSystemRoot(false),
166 wait_for_thread(fJobRunner
, NULL
);
171 Root::Init(const node_ref
& nodeRef
, bool isSystemRoot
)
174 fIsSystemRoot
= isSystemRoot
;
176 // init members and spawn job runner thread
177 status_t error
= fJobQueue
.Init();
181 error
= fLock
.InitCheck();
185 fJobRunner
= spawn_thread(&_JobRunnerEntry
, "job runner", B_NORMAL_PRIORITY
,
188 RETURN_ERROR(fJobRunner
);
191 BDirectory directory
;
192 error
= directory
.SetTo(&fNodeRef
);
194 ERROR("Root::Init(): failed to open directory: %s\n", strerror(error
));
199 error
= directory
.GetEntry(&entry
);
203 error
= entry
.GetPath(&path
);
206 ERROR("Root::Init(): failed to get directory path: %s\n",
213 RETURN_ERROR(B_NO_MEMORY
);
215 resume_thread(fJobRunner
);
222 Root::RegisterVolume(Volume
* volume
)
224 AutoLocker
<BLocker
> locker(fLock
);
226 Volume
** volumeToSet
= _GetVolume(volume
->MountType());
227 if (volumeToSet
== NULL
)
230 if (*volumeToSet
!= NULL
) {
231 ERROR("Root::RegisterVolume(): can't register volume at \"%s\", since "
232 "there's already volume at \"%s\" with the same type.\n",
233 volume
->Path().String(), (*volumeToSet
)->Path().String());
237 *volumeToSet
= volume
;
238 volume
->SetRoot(this);
240 // queue a job for reading the volume's packages
241 status_t error
= _QueueJob(
242 new(std::nothrow
) VolumeJob(volume
, &Root::_InitPackages
));
244 volume
->SetRoot(NULL
);
254 Root::UnregisterVolume(Volume
* volume
)
256 AutoLocker
<BLocker
> locker(fLock
);
258 Volume
** volumeToSet
= _GetVolume(volume
->MountType());
259 if (volumeToSet
== NULL
|| *volumeToSet
!= volume
) {
260 ERROR("Root::UnregisterVolume(): can't unregister unknown volume at "
261 "\"%s.\n", volume
->Path().String());
267 // Use the job queue to delete the volume to make sure there aren't any
268 // pending jobs that reference the volume.
269 _QueueJob(new(std::nothrow
) VolumeJob(volume
, &Root::_DeleteVolume
));
274 Root::FindVolume(dev_t deviceID
) const
276 AutoLocker
<BLocker
> locker(fLock
);
278 Volume
* volumes
[] = { fSystemVolume
, fHomeVolume
};
279 for (size_t i
= 0; i
< sizeof(volumes
) / sizeof(volumes
[0]); i
++) {
280 Volume
* volume
= volumes
[i
];
281 if (volume
!= NULL
&& volume
->DeviceID() == deviceID
)
290 Root::GetVolume(BPackageInstallationLocation location
)
292 switch ((BPackageInstallationLocation
)location
) {
293 case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM
:
294 return fSystemVolume
;
295 case B_PACKAGE_INSTALLATION_LOCATION_HOME
:
304 Root::HandleRequest(BMessage
* message
)
306 ObjectDeleter
<BMessage
> messageDeleter(message
);
308 // get the location and the volume
310 if (message
->FindInt32("location", &location
) != B_OK
312 || location
>= B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT
) {
316 AutoLocker
<BLocker
> locker(fLock
);
318 Volume
* volume
= GetVolume((BPackageInstallationLocation
)location
);
322 switch (message
->what
) {
323 case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO
:
324 volume
->HandleGetLocationInfoRequest(message
);
327 case B_MESSAGE_COMMIT_TRANSACTION
:
329 // The B_MESSAGE_COMMIT_TRANSACTION request must be handled in the
330 // job thread. But only queue a job, if there aren't package jobs
332 if (volume
->IsPackageJobPending()) {
333 BMessage
reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY
);
334 BCommitTransactionResult
result(
335 B_TRANSACTION_INSTALLATION_LOCATION_BUSY
);
336 if (result
.AddToMessage(reply
) == B_OK
) {
337 message
->SendReply(&reply
, (BHandler
*)NULL
,
338 kCommunicationTimeout
);
343 CommitTransactionJob
* job
= new(std::nothrow
) CommitTransactionJob(
344 this, volume
, message
);
348 messageDeleter
.Detach();
361 Root::VolumeNodeMonitorEventOccurred(Volume
* volume
)
363 _QueueJob(new(std::nothrow
) ProcessNodeMonitorEventsJob(volume
,
364 &Root::_ProcessNodeMonitorEvents
));
369 Root::LastReferenceReleased()
375 Root::_GetVolume(PackageFSMountType mountType
)
378 case PACKAGE_FS_MOUNT_TYPE_SYSTEM
:
379 return &fSystemVolume
;
380 case PACKAGE_FS_MOUNT_TYPE_HOME
:
382 case PACKAGE_FS_MOUNT_TYPE_CUSTOM
:
390 Root::_NextVolumeFor(Volume
* volume
)
395 PackageFSMountType mountType
= volume
->MountType();
399 case PACKAGE_FS_MOUNT_TYPE_HOME
:
400 mountType
= PACKAGE_FS_MOUNT_TYPE_SYSTEM
;
402 case PACKAGE_FS_MOUNT_TYPE_SYSTEM
:
403 case PACKAGE_FS_MOUNT_TYPE_CUSTOM
:
408 volume
= *_GetVolume(mountType
);
409 } while (volume
== NULL
);
416 Root::_InitPackages(Volume
* volume
)
418 if (volume
->InitPackages(this) == B_OK
) {
419 AutoLocker
<BLocker
> locker(fLock
);
420 Volume
* nextVolume
= _NextVolumeFor(volume
);
421 Volume
* nextNextVolume
= _NextVolumeFor(nextVolume
);
424 volume
->InitialVerify(nextVolume
, nextNextVolume
);
430 Root::_DeleteVolume(Volume
* volume
)
432 // delete all pending jobs for that volume
433 VolumeJobFilter
filter(volume
);
434 fJobQueue
.DeleteJobs(&filter
);
441 Root::_ProcessNodeMonitorEvents(Volume
* volume
)
443 volume
->ProcessPendingNodeMonitorEvents();
445 if (!volume
->HasPendingPackageActivationChanges())
448 // If this is not the system root, just activate/deactivate the packages.
449 if (!fIsSystemRoot
) {
450 volume
->ProcessPendingPackageActivationChanges();
454 // For the system root do the full dependency analysis.
456 PRINT("Root::_ProcessNodeMonitorEvents(): running package manager...\n");
458 PackageManager
packageManager(this, volume
);
459 packageManager
.HandleUserChanges();
460 } catch (BNothingToDoException
&) {
461 PRINT("Root::_ProcessNodeMonitorEvents(): -> nothing to do\n");
462 } catch (std::bad_alloc
&) {
464 "Insufficient memory while trying to apply package changes.");
465 } catch (BFatalErrorException
& exception
) {
466 if (exception
.Error() == B_OK
) {
467 _ShowError(exception
.Message());
469 _ShowError(BString().SetToFormat("%s: %s",
470 exception
.Message().String(), strerror(exception
.Error())));
472 // TODO: Print exception.Details()?
473 } catch (BAbortedByUserException
&) {
474 PRINT("Root::_ProcessNodeMonitorEvents(): -> aborted by user\n");
477 volume
->ClearPackageActivationChanges();
482 Root::_CommitTransaction(Volume
* volume
, BMessage
* message
)
484 volume
->HandleCommitTransactionRequest(message
);
489 Root::_QueueJob(Job
* job
)
494 BReference
<Job
> jobReference(job
, true);
495 if (!fJobQueue
.QueueJob(job
)) {
496 // job queue already closed
505 Root::_JobRunnerEntry(void* data
)
507 return ((Root
*)data
)->_JobRunner();
514 while (Job
* job
= fJobQueue
.DequeueJob()) {
516 job
->ReleaseReference();
524 Root::_ShowError(const char* errorMessage
)
526 BServer
* server
= dynamic_cast<BServer
*>(be_app
);
527 if (server
!= NULL
&& server
->InitGUIContext() == B_OK
) {
528 BAlert
* alert
= new(std::nothrow
) BAlert("Package error",
529 errorMessage
, "OK", NULL
, NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
531 alert
->SetShortcut(0, B_ESCAPE
);
537 ERROR("Root::_ShowError(): %s\n", errorMessage
);