1 /*
2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
7 */
8
9
10 #include "Root.h"
11
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>
18
19 #include <AutoDeleter.h>
20 #include <AutoLocker.h>
21 #include <Server.h>
22
23 #include <package/DaemonDefs.h>
24 #include <package/manager/Exceptions.h>
25
26 #include "Constants.h"
27 #include "DebugSupport.h"
28 #include "PackageManager.h"
29
30
31 using namespace BPackageKit::BPrivate;
32 using namespace BPackageKit::BManager::BPrivate;
33
34
35 // #pragma mark - AbstractVolumeJob
36
37
38 struct Root::AbstractVolumeJob : public Job {
AbstractVolumeJobRoot::AbstractVolumeJob39 AbstractVolumeJob(Volume* volume)
40 :
41 fVolume(volume)
42 {
43 }
44
GetVolumeRoot::AbstractVolumeJob45 Volume* GetVolume() const
46 {
47 return fVolume;
48 }
49
50 protected:
51 Volume* fVolume;
52 };
53
54
55 // #pragma mark - VolumeJob
56
57
58 struct Root::VolumeJob : public AbstractVolumeJob {
VolumeJobRoot::VolumeJob59 VolumeJob(Volume* volume, void (Root::*method)(Volume*))
60 :
61 AbstractVolumeJob(volume),
62 fMethod(method)
63 {
64 }
65
DoRoot::VolumeJob66 virtual void Do()
67 {
68 (fVolume->GetRoot()->*fMethod)(fVolume);
69 }
70
71 private:
72 void (Root::*fMethod)(Volume*);
73 };
74
75
76 // #pragma mark - ProcessNodeMonitorEventsJob
77
78
79 struct Root::ProcessNodeMonitorEventsJob : public VolumeJob {
ProcessNodeMonitorEventsJobRoot::ProcessNodeMonitorEventsJob80 ProcessNodeMonitorEventsJob(Volume* volume, void (Root::*method)(Volume*))
81 :
82 VolumeJob(volume, method)
83 {
84 fVolume->PackageJobPending();
85 }
86
~ProcessNodeMonitorEventsJobRoot::ProcessNodeMonitorEventsJob87 ~ProcessNodeMonitorEventsJob()
88 {
89 fVolume->PackageJobFinished();
90 }
91 };
92
93
94 // #pragma mark - CommitTransactionJob
95
96
97 struct Root::CommitTransactionJob : public AbstractVolumeJob {
CommitTransactionJobRoot::CommitTransactionJob98 CommitTransactionJob(Root* root, Volume* volume, BMessage* message)
99 :
100 AbstractVolumeJob(volume),
101 fRoot(root),
102 fMessage(message)
103 {
104 fVolume->PackageJobPending();
105 }
106
~CommitTransactionJobRoot::CommitTransactionJob107 ~CommitTransactionJob()
108 {
109 fVolume->PackageJobFinished();
110 }
111
DoRoot::CommitTransactionJob112 virtual void Do()
113 {
114 fRoot->_CommitTransaction(fVolume, fMessage.Get());
115 }
116
117 private:
118 Root* fRoot;
119 ObjectDeleter<BMessage> fMessage;
120 };
121
122
123 // #pragma mark - VolumeJobFilter
124
125
126 struct Root::VolumeJobFilter : public ::JobQueue::Filter {
VolumeJobFilterRoot::VolumeJobFilter127 VolumeJobFilter(Volume* volume)
128 :
129 fVolume(volume)
130 {
131 }
132
FilterJobRoot::VolumeJobFilter133 virtual bool FilterJob(Job* job)
134 {
135 AbstractVolumeJob* volumeJob = dynamic_cast<AbstractVolumeJob*>(job);
136 return volumeJob != NULL && volumeJob->GetVolume() == fVolume;
137 }
138
139 private:
140 Volume* fVolume;
141 };
142
143
144 // #pragma mark - Root
145
146
Root()147 Root::Root()
148 :
149 fLock("packagefs root"),
150 fNodeRef(),
151 fIsSystemRoot(false),
152 fPath(),
153 fSystemVolume(NULL),
154 fHomeVolume(NULL),
155 fJobQueue(),
156 fJobRunner(-1)
157 {
158 }
159
160
~Root()161 Root::~Root()
162 {
163 fJobQueue.Close();
164
165 if (fJobRunner >= 0)
166 wait_for_thread(fJobRunner, NULL);
167 }
168
169
170 status_t
Init(const node_ref & nodeRef,bool isSystemRoot)171 Root::Init(const node_ref& nodeRef, bool isSystemRoot)
172 {
173 fNodeRef = nodeRef;
174 fIsSystemRoot = isSystemRoot;
175
176 // init members and spawn job runner thread
177 status_t error = fJobQueue.Init();
178 if (error != B_OK)
179 RETURN_ERROR(error);
180
181 error = fLock.InitCheck();
182 if (error != B_OK)
183 RETURN_ERROR(error);
184
185 fJobRunner = spawn_thread(&_JobRunnerEntry, "job runner", B_NORMAL_PRIORITY,
186 this);
187 if (fJobRunner < 0)
188 RETURN_ERROR(fJobRunner);
189
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);
196 }
197
198 BEntry entry;
199 error = directory.GetEntry(&entry);
200
201 BPath path;
202 if (error == B_OK)
203 error = entry.GetPath(&path);
204
205 if (error != B_OK) {
206 ERROR("Root::Init(): failed to get directory path: %s\n",
207 strerror(error));
208 RETURN_ERROR(error);
209 }
210
211 fPath = path.Path();
212 if (fPath.IsEmpty())
213 RETURN_ERROR(B_NO_MEMORY);
214
215 resume_thread(fJobRunner);
216
217 return B_OK;
218 }
219
220
221 status_t
RegisterVolume(Volume * volume)222 Root::RegisterVolume(Volume* volume)
223 {
224 AutoLocker<BLocker> locker(fLock);
225
226 Volume** volumeToSet = _GetVolume(volume->MountType());
227 if (volumeToSet == NULL)
228 return B_BAD_VALUE;
229
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;
235 }
236
237 *volumeToSet = volume;
238 volume->SetRoot(this);
239
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;
247 }
248
249 return B_OK;
250 }
251
252
253 void
UnregisterVolume(Volume * volume)254 Root::UnregisterVolume(Volume* volume)
255 {
256 AutoLocker<BLocker> locker(fLock);
257
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;
263 }
264
265 *volumeToSet = NULL;
266
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));
270 }
271
272
273 Volume*
FindVolume(dev_t deviceID) const274 Root::FindVolume(dev_t deviceID) const
275 {
276 AutoLocker<BLocker> locker(fLock);
277
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;
283 }
284
285 return NULL;
286 }
287
288
289 Volume*
GetVolume(BPackageInstallationLocation location)290 Root::GetVolume(BPackageInstallationLocation location)
291 {
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;
299 }
300 }
301
302
303 void
HandleRequest(BMessage * message)304 Root::HandleRequest(BMessage* message)
305 {
306 ObjectDeleter<BMessage> messageDeleter(message);
307
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;
314 }
315
316 AutoLocker<BLocker> locker(fLock);
317
318 Volume* volume = GetVolume((BPackageInstallationLocation)location);
319 if (volume == NULL)
320 return;
321
322 switch (message->what) {
323 case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO:
324 volume->HandleGetLocationInfoRequest(message);
325 break;
326
327 case B_MESSAGE_COMMIT_TRANSACTION:
328 {
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);
339 }
340 return;
341 }
342
343 CommitTransactionJob* job = new(std::nothrow) CommitTransactionJob(
344 this, volume, message);
345 if (job == NULL)
346 return;
347
348 messageDeleter.Detach();
349
350 _QueueJob(job);
351 break;
352 }
353
354 default:
355 break;
356 }
357 }
358
359
360 void
VolumeNodeMonitorEventOccurred(Volume * volume)361 Root::VolumeNodeMonitorEventOccurred(Volume* volume)
362 {
363 _QueueJob(new(std::nothrow) ProcessNodeMonitorEventsJob(volume,
364 &Root::_ProcessNodeMonitorEvents));
365 }
366
367
368 void
LastReferenceReleased()369 Root::LastReferenceReleased()
370 {
371 }
372
373
374 Volume**
_GetVolume(PackageFSMountType mountType)375 Root::_GetVolume(PackageFSMountType mountType)
376 {
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;
385 }
386 }
387
388
389 Volume*
_NextVolumeFor(Volume * volume)390 Root::_NextVolumeFor(Volume* volume)
391 {
392 if (volume == NULL)
393 return NULL;
394
395 PackageFSMountType mountType = volume->MountType();
396
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;
406 }
407
408 volume = *_GetVolume(mountType);
409 } while (volume == NULL);
410
411 return volume;
412 }
413
414
415 void
_InitPackages(Volume * volume)416 Root::_InitPackages(Volume* volume)
417 {
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();
423
424 volume->InitialVerify(nextVolume, nextNextVolume);
425 }
426 }
427
428
429 void
_DeleteVolume(Volume * volume)430 Root::_DeleteVolume(Volume* volume)
431 {
432 // delete all pending jobs for that volume
433 VolumeJobFilter filter(volume);
434 fJobQueue.DeleteJobs(&filter);
435
436 delete volume;
437 }
438
439
440 void
_ProcessNodeMonitorEvents(Volume * volume)441 Root::_ProcessNodeMonitorEvents(Volume* volume)
442 {
443 volume->ProcessPendingNodeMonitorEvents();
444
445 if (!volume->HasPendingPackageActivationChanges())
446 return;
447
448 // If this is not the system root, just activate/deactivate the packages.
449 if (!fIsSystemRoot) {
450 volume->ProcessPendingPackageActivationChanges();
451 return;
452 }
453
454 // For the system root do the full dependency analysis.
455
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())));
471 }
472 // TODO: Print exception.Details()?
473 } catch (BAbortedByUserException&) {
474 PRINT("Root::_ProcessNodeMonitorEvents(): -> aborted by user\n");
475 }
476
477 volume->ClearPackageActivationChanges();
478 }
479
480
481 void
_CommitTransaction(Volume * volume,BMessage * message)482 Root::_CommitTransaction(Volume* volume, BMessage* message)
483 {
484 volume->HandleCommitTransactionRequest(message);
485 }
486
487
488 status_t
_QueueJob(Job * job)489 Root::_QueueJob(Job* job)
490 {
491 if (job == NULL)
492 return B_NO_MEMORY;
493
494 BReference<Job> jobReference(job, true);
495 if (!fJobQueue.QueueJob(job)) {
496 // job queue already closed
497 return B_BAD_VALUE;
498 }
499
500 return B_OK;
501 }
502
503
504 /*static*/ status_t
_JobRunnerEntry(void * data)505 Root::_JobRunnerEntry(void* data)
506 {
507 return ((Root*)data)->_JobRunner();
508 }
509
510
511 status_t
_JobRunner()512 Root::_JobRunner()
513 {
514 while (Job* job = fJobQueue.DequeueJob()) {
515 job->Do();
516 job->ReleaseReference();
517 }
518
519 return B_OK;
520 }
521
522
523 /*static*/ void
_ShowError(const char * errorMessage)524 Root::_ShowError(const char* errorMessage)
525 {
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;
534 }
535 }
536
537 ERROR("Root::_ShowError(): %s\n", errorMessage);
538 }
539