btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / servers / package / Root.cpp
blobddab4d5b95e40d0b1ff663e8a490e81fbffc7dd2
1 /*
2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
10 #include "Root.h"
12 #include <Alert.h>
13 #include <Directory.h>
14 #include <Entry.h>
15 #include <package/CommitTransactionResult.h>
16 #include <package/PackageDefs.h>
17 #include <Path.h>
19 #include <AutoDeleter.h>
20 #include <AutoLocker.h>
21 #include <Server.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)
41 fVolume(volume)
45 Volume* GetVolume() const
47 return fVolume;
50 protected:
51 Volume* fVolume;
55 // #pragma mark - VolumeJob
58 struct Root::VolumeJob : public AbstractVolumeJob {
59 VolumeJob(Volume* volume, void (Root::*method)(Volume*))
61 AbstractVolumeJob(volume),
62 fMethod(method)
66 virtual void Do()
68 (fVolume->GetRoot()->*fMethod)(fVolume);
71 private:
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),
101 fRoot(root),
102 fMessage(message)
104 fVolume->PackageJobPending();
107 ~CommitTransactionJob()
109 fVolume->PackageJobFinished();
112 virtual void Do()
114 fRoot->_CommitTransaction(fVolume, fMessage.Get());
117 private:
118 Root* fRoot;
119 ObjectDeleter<BMessage> fMessage;
123 // #pragma mark - VolumeJobFilter
126 struct Root::VolumeJobFilter : public ::JobQueue::Filter {
127 VolumeJobFilter(Volume* volume)
129 fVolume(volume)
133 virtual bool FilterJob(Job* job)
135 AbstractVolumeJob* volumeJob = dynamic_cast<AbstractVolumeJob*>(job);
136 return volumeJob != NULL && volumeJob->GetVolume() == fVolume;
139 private:
140 Volume* fVolume;
144 // #pragma mark - Root
147 Root::Root()
149 fLock("packagefs root"),
150 fNodeRef(),
151 fIsSystemRoot(false),
152 fPath(),
153 fSystemVolume(NULL),
154 fHomeVolume(NULL),
155 fJobQueue(),
156 fJobRunner(-1)
161 Root::~Root()
163 fJobQueue.Close();
165 if (fJobRunner >= 0)
166 wait_for_thread(fJobRunner, NULL);
170 status_t
171 Root::Init(const node_ref& nodeRef, bool isSystemRoot)
173 fNodeRef = nodeRef;
174 fIsSystemRoot = isSystemRoot;
176 // init members and spawn job runner thread
177 status_t error = fJobQueue.Init();
178 if (error != B_OK)
179 RETURN_ERROR(error);
181 error = fLock.InitCheck();
182 if (error != B_OK)
183 RETURN_ERROR(error);
185 fJobRunner = spawn_thread(&_JobRunnerEntry, "job runner", B_NORMAL_PRIORITY,
186 this);
187 if (fJobRunner < 0)
188 RETURN_ERROR(fJobRunner);
190 // get the path
191 BDirectory directory;
192 error = directory.SetTo(&fNodeRef);
193 if (error != B_OK) {
194 ERROR("Root::Init(): failed to open directory: %s\n", strerror(error));
195 RETURN_ERROR(error);
198 BEntry entry;
199 error = directory.GetEntry(&entry);
201 BPath path;
202 if (error == B_OK)
203 error = entry.GetPath(&path);
205 if (error != B_OK) {
206 ERROR("Root::Init(): failed to get directory path: %s\n",
207 strerror(error));
208 RETURN_ERROR(error);
211 fPath = path.Path();
212 if (fPath.IsEmpty())
213 RETURN_ERROR(B_NO_MEMORY);
215 resume_thread(fJobRunner);
217 return B_OK;
221 status_t
222 Root::RegisterVolume(Volume* volume)
224 AutoLocker<BLocker> locker(fLock);
226 Volume** volumeToSet = _GetVolume(volume->MountType());
227 if (volumeToSet == NULL)
228 return B_BAD_VALUE;
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());
234 return B_BAD_VALUE;
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));
243 if (error != B_OK) {
244 volume->SetRoot(NULL);
245 *volumeToSet = NULL;
246 return error;
249 return B_OK;
253 void
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());
262 return;
265 *volumeToSet = NULL;
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));
273 Volume*
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)
282 return volume;
285 return NULL;
289 Volume*
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:
296 return fHomeVolume;
297 default:
298 return NULL;
303 void
304 Root::HandleRequest(BMessage* message)
306 ObjectDeleter<BMessage> messageDeleter(message);
308 // get the location and the volume
309 int32 location;
310 if (message->FindInt32("location", &location) != B_OK
311 || location < 0
312 || location >= B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT) {
313 return;
316 AutoLocker<BLocker> locker(fLock);
318 Volume* volume = GetVolume((BPackageInstallationLocation)location);
319 if (volume == NULL)
320 return;
322 switch (message->what) {
323 case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO:
324 volume->HandleGetLocationInfoRequest(message);
325 break;
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
331 // pending already.
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);
340 return;
343 CommitTransactionJob* job = new(std::nothrow) CommitTransactionJob(
344 this, volume, message);
345 if (job == NULL)
346 return;
348 messageDeleter.Detach();
350 _QueueJob(job);
351 break;
354 default:
355 break;
360 void
361 Root::VolumeNodeMonitorEventOccurred(Volume* volume)
363 _QueueJob(new(std::nothrow) ProcessNodeMonitorEventsJob(volume,
364 &Root::_ProcessNodeMonitorEvents));
368 void
369 Root::LastReferenceReleased()
374 Volume**
375 Root::_GetVolume(PackageFSMountType mountType)
377 switch (mountType) {
378 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
379 return &fSystemVolume;
380 case PACKAGE_FS_MOUNT_TYPE_HOME:
381 return &fHomeVolume;
382 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
383 default:
384 return NULL;
389 Volume*
390 Root::_NextVolumeFor(Volume* volume)
392 if (volume == NULL)
393 return NULL;
395 PackageFSMountType mountType = volume->MountType();
397 do {
398 switch (mountType) {
399 case PACKAGE_FS_MOUNT_TYPE_HOME:
400 mountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM;
401 break;
402 case PACKAGE_FS_MOUNT_TYPE_SYSTEM:
403 case PACKAGE_FS_MOUNT_TYPE_CUSTOM:
404 default:
405 return NULL;
408 volume = *_GetVolume(mountType);
409 } while (volume == NULL);
411 return volume;
415 void
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);
422 locker.Unlock();
424 volume->InitialVerify(nextVolume, nextNextVolume);
429 void
430 Root::_DeleteVolume(Volume* volume)
432 // delete all pending jobs for that volume
433 VolumeJobFilter filter(volume);
434 fJobQueue.DeleteJobs(&filter);
436 delete volume;
440 void
441 Root::_ProcessNodeMonitorEvents(Volume* volume)
443 volume->ProcessPendingNodeMonitorEvents();
445 if (!volume->HasPendingPackageActivationChanges())
446 return;
448 // If this is not the system root, just activate/deactivate the packages.
449 if (!fIsSystemRoot) {
450 volume->ProcessPendingPackageActivationChanges();
451 return;
454 // For the system root do the full dependency analysis.
456 PRINT("Root::_ProcessNodeMonitorEvents(): running package manager...\n");
457 try {
458 PackageManager packageManager(this, volume);
459 packageManager.HandleUserChanges();
460 } catch (BNothingToDoException&) {
461 PRINT("Root::_ProcessNodeMonitorEvents(): -> nothing to do\n");
462 } catch (std::bad_alloc&) {
463 _ShowError(
464 "Insufficient memory while trying to apply package changes.");
465 } catch (BFatalErrorException& exception) {
466 if (exception.Error() == B_OK) {
467 _ShowError(exception.Message());
468 } else {
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();
481 void
482 Root::_CommitTransaction(Volume* volume, BMessage* message)
484 volume->HandleCommitTransactionRequest(message);
488 status_t
489 Root::_QueueJob(Job* job)
491 if (job == NULL)
492 return B_NO_MEMORY;
494 BReference<Job> jobReference(job, true);
495 if (!fJobQueue.QueueJob(job)) {
496 // job queue already closed
497 return B_BAD_VALUE;
500 return B_OK;
504 /*static*/ status_t
505 Root::_JobRunnerEntry(void* data)
507 return ((Root*)data)->_JobRunner();
511 status_t
512 Root::_JobRunner()
514 while (Job* job = fJobQueue.DequeueJob()) {
515 job->Do();
516 job->ReleaseReference();
519 return B_OK;
523 /*static*/ void
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);
530 if (alert != NULL) {
531 alert->SetShortcut(0, B_ESCAPE);
532 alert->Go();
533 return;
537 ERROR("Root::_ShowError(): %s\n", errorMessage);