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