1 /* 2 * Copyright 2013, 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/PackageDefs.h> 16 #include <Path.h> 17 18 #include <AutoDeleter.h> 19 #include <AutoLocker.h> 20 #include <Server.h> 21 22 #include <package/DaemonDefs.h> 23 #include <package/manager/Exceptions.h> 24 25 #include "Constants.h" 26 #include "DebugSupport.h" 27 #include "PackageManager.h" 28 29 30 using namespace BPackageKit::BPrivate; 31 using namespace BPackageKit::BManager::BPrivate; 32 33 34 // #pragma mark - AbstractVolumeJob 35 36 37 struct Root::AbstractVolumeJob : public Job { 38 AbstractVolumeJob(Volume* volume) 39 : 40 fVolume(volume) 41 { 42 } 43 44 Volume* GetVolume() const 45 { 46 return fVolume; 47 } 48 49 protected: 50 Volume* fVolume; 51 }; 52 53 54 // #pragma mark - VolumeJob 55 56 57 struct Root::VolumeJob : public AbstractVolumeJob { 58 VolumeJob(Volume* volume, void (Root::*method)(Volume*)) 59 : 60 AbstractVolumeJob(volume), 61 fMethod(method) 62 { 63 } 64 65 virtual void Do() 66 { 67 (fVolume->GetRoot()->*fMethod)(fVolume); 68 } 69 70 private: 71 void (Root::*fMethod)(Volume*); 72 }; 73 74 75 // #pragma mark - ProcessNodeMonitorEventsJob 76 77 78 struct Root::ProcessNodeMonitorEventsJob : public VolumeJob { 79 ProcessNodeMonitorEventsJob(Volume* volume, void (Root::*method)(Volume*)) 80 : 81 VolumeJob(volume, method) 82 { 83 fVolume->PackageJobPending(); 84 } 85 86 ~ProcessNodeMonitorEventsJob() 87 { 88 fVolume->PackageJobFinished(); 89 } 90 }; 91 92 93 // #pragma mark - CommitTransactionJob 94 95 96 struct Root::CommitTransactionJob : public AbstractVolumeJob { 97 CommitTransactionJob(Root* root, Volume* volume, BMessage* message) 98 : 99 AbstractVolumeJob(volume), 100 fRoot(root), 101 fMessage(message) 102 { 103 fVolume->PackageJobPending(); 104 } 105 106 ~CommitTransactionJob() 107 { 108 fVolume->PackageJobFinished(); 109 } 110 111 virtual void Do() 112 { 113 fRoot->_CommitTransaction(fVolume, fMessage.Get()); 114 } 115 116 private: 117 Root* fRoot; 118 ObjectDeleter<BMessage> fMessage; 119 }; 120 121 122 // #pragma mark - VolumeJobFilter 123 124 125 struct Root::VolumeJobFilter : public ::JobQueue::Filter { 126 VolumeJobFilter(Volume* volume) 127 : 128 fVolume(volume) 129 { 130 } 131 132 virtual bool FilterJob(Job* job) 133 { 134 AbstractVolumeJob* volumeJob = dynamic_cast<AbstractVolumeJob*>(job); 135 return volumeJob != NULL && volumeJob->GetVolume() == fVolume; 136 } 137 138 private: 139 Volume* fVolume; 140 }; 141 142 143 // #pragma mark - Root 144 145 146 Root::Root() 147 : 148 fLock("packagefs root"), 149 fNodeRef(), 150 fIsSystemRoot(false), 151 fPath(), 152 fSystemVolume(NULL), 153 fHomeVolume(NULL), 154 fJobQueue(), 155 fJobRunner(-1) 156 { 157 } 158 159 160 Root::~Root() 161 { 162 fJobQueue.Close(); 163 164 if (fJobRunner >= 0) 165 wait_for_thread(fJobRunner, NULL); 166 } 167 168 169 status_t 170 Root::Init(const node_ref& nodeRef, bool isSystemRoot) 171 { 172 fNodeRef = nodeRef; 173 fIsSystemRoot = isSystemRoot; 174 175 // init members and spawn job runner thread 176 status_t error = fJobQueue.Init(); 177 if (error != B_OK) 178 RETURN_ERROR(error); 179 180 error = fLock.InitCheck(); 181 if (error != B_OK) 182 RETURN_ERROR(error); 183 184 fJobRunner = spawn_thread(&_JobRunnerEntry, "job runner", B_NORMAL_PRIORITY, 185 this); 186 if (fJobRunner < 0) 187 RETURN_ERROR(fJobRunner); 188 189 // get the path 190 BDirectory directory; 191 error = directory.SetTo(&fNodeRef); 192 if (error != B_OK) { 193 ERROR("Root::Init(): failed to open directory: %s\n", strerror(error)); 194 RETURN_ERROR(error); 195 } 196 197 BEntry entry; 198 error = directory.GetEntry(&entry); 199 200 BPath path; 201 if (error == B_OK) 202 error = entry.GetPath(&path); 203 204 if (error != B_OK) { 205 ERROR("Root::Init(): failed to get directory path: %s\n", 206 strerror(error)); 207 RETURN_ERROR(error); 208 } 209 210 fPath = path.Path(); 211 if (fPath.IsEmpty()) 212 RETURN_ERROR(B_NO_MEMORY); 213 214 resume_thread(fJobRunner); 215 216 return B_OK; 217 } 218 219 220 status_t 221 Root::RegisterVolume(Volume* volume) 222 { 223 AutoLocker<BLocker> locker(fLock); 224 225 Volume** volumeToSet = _GetVolume(volume->MountType()); 226 if (volumeToSet == NULL) 227 return B_BAD_VALUE; 228 229 if (*volumeToSet != NULL) { 230 ERROR("Root::RegisterVolume(): can't register volume at \"%s\", since " 231 "there's already volume at \"%s\" with the same type.\n", 232 volume->Path().String(), (*volumeToSet)->Path().String()); 233 return B_BAD_VALUE; 234 } 235 236 *volumeToSet = volume; 237 volume->SetRoot(this); 238 239 // queue a job for reading the volume's packages 240 status_t error = _QueueJob( 241 new(std::nothrow) VolumeJob(volume, &Root::_InitPackages)); 242 if (error != B_OK) { 243 volume->SetRoot(NULL); 244 *volumeToSet = NULL; 245 return error; 246 } 247 248 return B_OK; 249 } 250 251 252 void 253 Root::UnregisterVolume(Volume* volume) 254 { 255 AutoLocker<BLocker> locker(fLock); 256 257 Volume** volumeToSet = _GetVolume(volume->MountType()); 258 if (volumeToSet == NULL || *volumeToSet != volume) { 259 ERROR("Root::UnregisterVolume(): can't unregister unknown volume at " 260 "\"%s.\n", volume->Path().String()); 261 return; 262 } 263 264 *volumeToSet = NULL; 265 266 // Use the job queue to delete the volume to make sure there aren't any 267 // pending jobs that reference the volume. 268 _QueueJob(new(std::nothrow) VolumeJob(volume, &Root::_DeleteVolume)); 269 } 270 271 272 Volume* 273 Root::FindVolume(dev_t deviceID) const 274 { 275 AutoLocker<BLocker> locker(fLock); 276 277 Volume* volumes[] = { fSystemVolume, fHomeVolume }; 278 for (size_t i = 0; i < sizeof(volumes) / sizeof(volumes[0]); i++) { 279 Volume* volume = volumes[i]; 280 if (volume != NULL && volume->DeviceID() == deviceID) 281 return volume; 282 } 283 284 return NULL; 285 } 286 287 288 Volume* 289 Root::GetVolume(BPackageInstallationLocation location) 290 { 291 switch ((BPackageInstallationLocation)location) { 292 case B_PACKAGE_INSTALLATION_LOCATION_SYSTEM: 293 return fSystemVolume; 294 case B_PACKAGE_INSTALLATION_LOCATION_HOME: 295 return fHomeVolume; 296 default: 297 return NULL; 298 } 299 } 300 301 302 void 303 Root::HandleRequest(BMessage* message) 304 { 305 ObjectDeleter<BMessage> messageDeleter(message); 306 307 // get the location and the volume 308 int32 location; 309 if (message->FindInt32("location", &location) != B_OK 310 || location < 0 311 || location >= B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT) { 312 return; 313 } 314 315 AutoLocker<BLocker> locker(fLock); 316 317 Volume* volume = GetVolume((BPackageInstallationLocation)location); 318 if (volume == NULL) 319 return; 320 321 switch (message->what) { 322 case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO: 323 volume->HandleGetLocationInfoRequest(message); 324 break; 325 326 case B_MESSAGE_COMMIT_TRANSACTION: 327 { 328 // The B_MESSAGE_COMMIT_TRANSACTION request must be handled in the 329 // job thread. But only queue a job, if there aren't package jobs 330 // pending already. 331 if (volume->IsPackageJobPending()) { 332 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); 333 if (reply.AddInt32("error", B_DAEMON_INSTALLATION_LOCATION_BUSY) 334 == B_OK) { 335 message->SendReply(&reply, (BHandler*)NULL, 336 kCommunicationTimeout); 337 } 338 return; 339 } 340 341 CommitTransactionJob* job = new(std::nothrow) CommitTransactionJob( 342 this, volume, message); 343 if (job == NULL) 344 return; 345 346 messageDeleter.Detach(); 347 348 _QueueJob(job); 349 break; 350 } 351 352 default: 353 break; 354 } 355 } 356 357 358 void 359 Root::VolumeNodeMonitorEventOccurred(Volume* volume) 360 { 361 _QueueJob(new(std::nothrow) ProcessNodeMonitorEventsJob(volume, 362 &Root::_ProcessNodeMonitorEvents)); 363 } 364 365 366 void 367 Root::LastReferenceReleased() 368 { 369 } 370 371 372 Volume** 373 Root::_GetVolume(PackageFSMountType mountType) 374 { 375 switch (mountType) { 376 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 377 return &fSystemVolume; 378 case PACKAGE_FS_MOUNT_TYPE_HOME: 379 return &fHomeVolume; 380 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 381 default: 382 return NULL; 383 } 384 } 385 386 387 Volume* 388 Root::_NextVolumeFor(Volume* volume) 389 { 390 if (volume == NULL) 391 return NULL; 392 393 PackageFSMountType mountType = volume->MountType(); 394 395 do { 396 switch (mountType) { 397 case PACKAGE_FS_MOUNT_TYPE_HOME: 398 mountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM; 399 break; 400 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 401 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 402 default: 403 return NULL; 404 } 405 406 volume = *_GetVolume(mountType); 407 } while (volume == NULL); 408 409 return volume; 410 } 411 412 413 void 414 Root::_InitPackages(Volume* volume) 415 { 416 if (volume->InitPackages(this) == B_OK) { 417 AutoLocker<BLocker> locker(fLock); 418 Volume* nextVolume = _NextVolumeFor(volume); 419 Volume* nextNextVolume = _NextVolumeFor(nextVolume); 420 locker.Unlock(); 421 422 volume->InitialVerify(nextVolume, nextNextVolume); 423 } 424 } 425 426 427 void 428 Root::_DeleteVolume(Volume* volume) 429 { 430 // delete all pending jobs for that volume 431 VolumeJobFilter filter(volume); 432 fJobQueue.DeleteJobs(&filter); 433 434 delete volume; 435 } 436 437 438 void 439 Root::_ProcessNodeMonitorEvents(Volume* volume) 440 { 441 volume->ProcessPendingNodeMonitorEvents(); 442 443 if (!volume->HasPendingPackageActivationChanges()) 444 return; 445 446 // If this is not the system root, just activate/deactivate the packages. 447 if (!fIsSystemRoot) { 448 volume->ProcessPendingPackageActivationChanges(); 449 return; 450 } 451 452 // For the system root do the full dependency analysis. 453 454 PRINT("Root::_ProcessNodeMonitorEvents(): running package manager...\n"); 455 try { 456 PackageManager packageManager(this, volume); 457 packageManager.HandleUserChanges(); 458 } catch (BNothingToDoException&) { 459 PRINT("Root::_ProcessNodeMonitorEvents(): -> nothing to do\n"); 460 } catch (std::bad_alloc&) { 461 _ShowError( 462 "Insufficient memory while trying to apply package changes."); 463 } catch (BFatalErrorException& exception) { 464 if (exception.Error() == B_OK) { 465 _ShowError(exception.Message()); 466 } else { 467 _ShowError(BString().SetToFormat("%s: %s", 468 exception.Message().String(), strerror(exception.Error()))); 469 } 470 // TODO: Print exception.Details()? 471 } catch (BAbortedByUserException&) { 472 PRINT("Root::_ProcessNodeMonitorEvents(): -> aborted by user\n"); 473 } 474 475 volume->ClearPackageActivationChanges(); 476 } 477 478 479 void 480 Root::_CommitTransaction(Volume* volume, BMessage* message) 481 { 482 volume->HandleCommitTransactionRequest(message); 483 } 484 485 486 status_t 487 Root::_QueueJob(Job* job) 488 { 489 if (job == NULL) 490 return B_NO_MEMORY; 491 492 BReference<Job> jobReference(job, true); 493 if (!fJobQueue.QueueJob(job)) { 494 // job queue already closed 495 return B_BAD_VALUE; 496 } 497 498 return B_OK; 499 } 500 501 502 /*static*/ status_t 503 Root::_JobRunnerEntry(void* data) 504 { 505 return ((Root*)data)->_JobRunner(); 506 } 507 508 509 status_t 510 Root::_JobRunner() 511 { 512 while (Job* job = fJobQueue.DequeueJob()) { 513 job->Do(); 514 job->ReleaseReference(); 515 } 516 517 return B_OK; 518 } 519 520 521 /*static*/ void 522 Root::_ShowError(const char* errorMessage) 523 { 524 BServer* server = dynamic_cast<BServer*>(be_app); 525 if (server != NULL && server->InitGUIContext() == B_OK) { 526 BAlert* alert = new(std::nothrow) BAlert("Package error", 527 errorMessage, "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 528 if (alert != NULL) { 529 alert->SetShortcut(0, B_ESCAPE); 530 alert->Go(); 531 return; 532 } 533 } 534 535 ERROR("Root::_ShowError(): %s\n", errorMessage); 536 } 537