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 { 39 AbstractVolumeJob(Volume* volume) 40 : 41 fVolume(volume) 42 { 43 } 44 45 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 { 59 VolumeJob(Volume* volume, void (Root::*method)(Volume*)) 60 : 61 AbstractVolumeJob(volume), 62 fMethod(method) 63 { 64 } 65 66 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 { 80 ProcessNodeMonitorEventsJob(Volume* volume, void (Root::*method)(Volume*)) 81 : 82 VolumeJob(volume, method) 83 { 84 fVolume->PackageJobPending(); 85 } 86 87 ~ProcessNodeMonitorEventsJob() 88 { 89 fVolume->PackageJobFinished(); 90 } 91 }; 92 93 94 // #pragma mark - CommitTransactionJob 95 96 97 struct Root::CommitTransactionJob : public AbstractVolumeJob { 98 CommitTransactionJob(Root* root, Volume* volume, BMessage* message) 99 : 100 AbstractVolumeJob(volume), 101 fRoot(root), 102 fMessage(message) 103 { 104 fVolume->PackageJobPending(); 105 } 106 107 ~CommitTransactionJob() 108 { 109 fVolume->PackageJobFinished(); 110 } 111 112 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 { 127 VolumeJobFilter(Volume* volume) 128 : 129 fVolume(volume) 130 { 131 } 132 133 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 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 161 Root::~Root() 162 { 163 fJobQueue.Close(); 164 165 if (fJobRunner >= 0) 166 wait_for_thread(fJobRunner, NULL); 167 } 168 169 170 status_t 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 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 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* 274 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* 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 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 361 Root::VolumeNodeMonitorEventOccurred(Volume* volume) 362 { 363 _QueueJob(new(std::nothrow) ProcessNodeMonitorEventsJob(volume, 364 &Root::_ProcessNodeMonitorEvents)); 365 } 366 367 368 void 369 Root::LastReferenceReleased() 370 { 371 } 372 373 374 Volume** 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* 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 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 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 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 482 Root::_CommitTransaction(Volume* volume, BMessage* message) 483 { 484 volume->HandleCommitTransactionRequest(message); 485 } 486 487 488 status_t 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 505 Root::_JobRunnerEntry(void* data) 506 { 507 return ((Root*)data)->_JobRunner(); 508 } 509 510 511 status_t 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 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