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